Updates kuryr_cni vendor folder

Updates "github.com/containernetworking/cni" version to 0.8.0

Change-Id: Iadcf9579ea9dae928ea5b416401d2b2e93115859
This commit is contained in:
Tabitha 2020-11-04 13:20:25 +01:00
parent 7b65148ed1
commit 99f9e0e639
14 changed files with 300 additions and 129 deletions

13
kuryr_cni/Gopkg.lock generated
View File

@ -2,26 +2,27 @@
[[projects]] [[projects]]
digest = "1:217f54a01004e15731471f4b3d420c16b99ade200d9872d7c1f0d2684df60f14" digest = "1:573af69cb9454f7475a43834ad6efd15185df23a722792ed666ab2d23b16722b"
name = "github.com/containernetworking/cni" name = "github.com/containernetworking/cni"
packages = [ packages = [
"pkg/skel", "pkg/skel",
"pkg/types", "pkg/types",
"pkg/types/020", "pkg/types/020",
"pkg/types/current", "pkg/types/current",
"pkg/utils",
"pkg/version", "pkg/version",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "7d76556571b6cf1ab90d7026a73092ac8d5e0c23" revision = "11db36c11b015c1d84fa6f46496796d193fa8d43"
version = "v0.7.0" version = "v0.8.0"
[[projects]] [[projects]]
digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" digest = "1:9e1d37b58d17113ec3cb5608ac0382313c5b59470b94ed97d0976e69c7022314"
name = "github.com/pkg/errors" name = "github.com/pkg/errors"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" revision = "614d223910a179a466c1767a985424175c39b465"
version = "v0.8.1" version = "v0.9.1"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"

View File

@ -27,7 +27,7 @@
[[constraint]] [[constraint]]
name = "github.com/containernetworking/cni" name = "github.com/containernetworking/cni"
version = "0.7.0" version = "0.8.0"
[prune] [prune]
go-tests = true go-tests = true

View File

@ -27,6 +27,7 @@ import (
"strings" "strings"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/utils"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
) )
@ -53,16 +54,7 @@ type dispatcher struct {
type reqForCmdEntry map[string]bool type reqForCmdEntry map[string]bool
// internal only error to indicate lack of required environment variables func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
type missingEnvError struct {
msg string
}
func (e missingEnvError) Error() string {
return e.msg
}
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
var cmd, contID, netns, ifName, args, path string var cmd, contID, netns, ifName, args, path string
vars := []struct { vars := []struct {
@ -138,7 +130,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
if len(argsMissing) > 0 { if len(argsMissing) > 0 {
joined := strings.Join(argsMissing, ",") joined := strings.Join(argsMissing, ",")
return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)} return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "")
} }
if cmd == "VERSION" { if cmd == "VERSION" {
@ -147,7 +139,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
stdinData, err := ioutil.ReadAll(t.Stdin) stdinData, err := ioutil.ReadAll(t.Stdin)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("error reading from stdin: %v", err) return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "")
} }
cmdArgs := &CmdArgs{ cmdArgs := &CmdArgs{
@ -161,39 +153,39 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
return cmd, cmdArgs, nil return cmd, cmdArgs, nil
} }
func createTypedError(f string, args ...interface{}) *types.Error { func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error {
return &types.Error{
Code: 100,
Msg: fmt.Sprintf(f, args...),
}
}
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error {
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil { if err != nil {
return err return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} }
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
if verErr != nil { if verErr != nil {
return &types.Error{ return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details())
Code: types.ErrIncompatibleCNIVersion,
Msg: "incompatible CNI versions",
Details: verErr.Details(),
}
} }
return toCall(cmdArgs) if err = toCall(cmdArgs); err != nil {
if e, ok := err.(*types.Error); ok {
// don't wrap Error in Error
return e
}
return types.NewError(types.ErrInternal, err.Error(), "")
}
return nil
} }
func validateConfig(jsonBytes []byte) error { func validateConfig(jsonBytes []byte) *types.Error {
var conf struct { var conf struct {
Name string `json:"name"` Name string `json:"name"`
} }
if err := json.Unmarshal(jsonBytes, &conf); err != nil { if err := json.Unmarshal(jsonBytes, &conf); err != nil {
return fmt.Errorf("error reading network config: %s", err) return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "")
} }
if conf.Name == "" { if conf.Name == "" {
return fmt.Errorf("missing network name") return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "")
}
if err := utils.ValidateNetworkName(conf.Name); err != nil {
return err
} }
return nil return nil
} }
@ -202,17 +194,22 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
cmd, cmdArgs, err := t.getCmdArgsFromEnv() cmd, cmdArgs, err := t.getCmdArgsFromEnv()
if err != nil { if err != nil {
// Print the about string to stderr when no command is set // Print the about string to stderr when no command is set
if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" { if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" {
fmt.Fprintln(t.Stderr, about) _, _ = fmt.Fprintln(t.Stderr, about)
return nil return nil
} }
return createTypedError(err.Error()) return err
} }
if cmd != "VERSION" { if cmd != "VERSION" {
err = validateConfig(cmdArgs.StdinData) if err = validateConfig(cmdArgs.StdinData); err != nil {
if err != nil { return err
return createTypedError(err.Error()) }
if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil {
return err
}
if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil {
return err
} }
} }
@ -222,45 +219,37 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
case "CHECK": case "CHECK":
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
if err != nil { if err != nil {
return createTypedError(err.Error()) return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} }
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil {
return createTypedError(err.Error()) return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if !gtet { } else if !gtet {
return &types.Error{ return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "")
Code: types.ErrIncompatibleCNIVersion,
Msg: "config version does not allow CHECK",
}
} }
for _, pluginVersion := range versionInfo.SupportedVersions() { for _, pluginVersion := range versionInfo.SupportedVersions() {
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
if err != nil { if err != nil {
return createTypedError(err.Error()) return types.NewError(types.ErrDecodingFailure, err.Error(), "")
} else if gtet { } else if gtet {
if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil {
return createTypedError(err.Error()) return err
} }
return nil return nil
} }
} }
return &types.Error{ return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "")
Code: types.ErrIncompatibleCNIVersion,
Msg: "plugin version does not allow CHECK",
}
case "DEL": case "DEL":
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
case "VERSION": case "VERSION":
err = versionInfo.Encode(t.Stdout) if err := versionInfo.Encode(t.Stdout); err != nil {
return types.NewError(types.ErrIOFailure, err.Error(), "")
}
default: default:
return createTypedError("unknown CNI_COMMAND: %v", cmd) return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "")
} }
if err != nil { if err != nil {
if e, ok := err.(*types.Error); ok { return err
// don't wrap Error in Error
return e
}
return createTypedError(err.Error())
} }
return nil return nil
} }

View File

@ -86,20 +86,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
return err return err
} }
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
func (r *Result) String() string {
var str string
if r.IP4 != nil {
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
}
if r.IP6 != nil {
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
}
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
}
// IPConfig contains values necessary to configure an interface // IPConfig contains values necessary to configure an interface
type IPConfig struct { type IPConfig struct {
IP net.IPNet IP net.IPNet

View File

@ -36,7 +36,7 @@ func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
case "0", "false": case "0", "false":
*b = false *b = false
default: default:
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s) return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
} }
return nil return nil
} }

View File

@ -207,23 +207,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
return err return err
} }
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
func (r *Result) String() string {
var str string
if len(r.Interfaces) > 0 {
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
}
if len(r.IPs) > 0 {
str += fmt.Sprintf("IP:%+v, ", r.IPs)
}
if len(r.Routes) > 0 {
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
}
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
}
// Convert this old version result to the current CNI version result // Convert this old version result to the current CNI version result
func (r *Result) Convert() (*Result, error) { func (r *Result) Convert() (*Result, error) {
return r, nil return r, nil

View File

@ -16,7 +16,6 @@ package types
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -101,9 +100,6 @@ type Result interface {
// Prints the result in JSON format to provided writer // Prints the result in JSON format to provided writer
PrintTo(writer io.Writer) error PrintTo(writer io.Writer) error
// Returns a JSON string representation of the result
String() string
} }
func PrintResult(result Result, version string) error { func PrintResult(result Result, version string) error {
@ -134,9 +130,16 @@ func (r *Route) String() string {
// Well known error codes // Well known error codes
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
const ( const (
ErrUnknown uint = iota // 0 ErrUnknown uint = iota // 0
ErrIncompatibleCNIVersion // 1 ErrIncompatibleCNIVersion // 1
ErrUnsupportedField // 2 ErrUnsupportedField // 2
ErrUnknownContainer // 3
ErrInvalidEnvironmentVariables // 4
ErrIOFailure // 5
ErrDecodingFailure // 6
ErrInvalidNetworkConfig // 7
ErrTryAgainLater uint = 11
ErrInternal uint = 999
) )
type Error struct { type Error struct {
@ -145,6 +148,14 @@ type Error struct {
Details string `json:"details,omitempty"` Details string `json:"details,omitempty"`
} }
func NewError(code uint, msg, details string) *Error {
return &Error{
Code: code,
Msg: msg,
Details: details,
}
}
func (e *Error) Error() string { func (e *Error) Error() string {
details := "" details := ""
if e.Details != "" { if e.Details != "" {
@ -194,6 +205,3 @@ func prettyPrint(obj interface{}) error {
_, err = os.Stdout.Write(data) _, err = os.Stdout.Write(data)
return err return err
} }
// NotImplementedError is used to indicate that a method is not implemented for the given platform
var NotImplementedError = errors.New("Not Implemented")

View File

@ -0,0 +1,84 @@
// Copyright 2019 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import (
"bytes"
"fmt"
"regexp"
"unicode"
"github.com/containernetworking/cni/pkg/types"
)
const (
// cniValidNameChars is the regexp used to validate valid characters in
// containerID and networkName
cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
// maxInterfaceNameLength is the length max of a valid interface name
maxInterfaceNameLength = 15
)
var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`)
// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters
func ValidateContainerID(containerID string) *types.Error {
if containerID == "" {
return types.NewError(types.ErrUnknownContainer, "missing containerID", "")
}
if !cniReg.MatchString(containerID) {
return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID)
}
return nil
}
// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters
func ValidateNetworkName(networkName string) *types.Error {
if networkName == "" {
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "")
}
if !cniReg.MatchString(networkName) {
return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName)
}
return nil
}
// ValidateInterfaceName will validate the interface name based on the three rules below
// 1. The name must not be empty
// 2. The name must be less than 16 characters
// 3. The name must not be "." or ".."
// 3. The name must not contain / or : or any whitespace characters
// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024
func ValidateInterfaceName(ifName string) *types.Error {
if len(ifName) == 0 {
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "")
}
if len(ifName) > maxInterfaceNameLength {
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1))
}
if ifName == "." || ifName == ".." {
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "")
}
for _, r := range bytes.Runes([]byte(ifName)) {
if r == '/' || r == ':' || unicode.IsSpace(r) {
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "")
}
}
return nil
}

View File

@ -1,15 +1,10 @@
language: go language: go
go_import_path: github.com/pkg/errors go_import_path: github.com/pkg/errors
go: go:
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x - 1.11.x
- 1.12.x
- 1.13.x
- tip - tip
script: script:
- go test -v ./... - make check

44
kuryr_cni/vendor/github.com/pkg/errors/Makefile generated vendored Normal file
View File

@ -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)"

View File

@ -41,11 +41,18 @@ default:
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). [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 ## 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.
## License ## License

View File

@ -82,7 +82,7 @@
// //
// if err, ok := err.(stackTracer); ok { // if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() { // for _, f := range err.StackTrace() {
// fmt.Printf("%+s:%d", f) // fmt.Printf("%+s:%d\n", f, f)
// } // }
// } // }
// //
@ -159,6 +159,9 @@ type withStack struct {
func (w *withStack) Cause() error { return w.error } 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) { func (w *withStack) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':
@ -241,6 +244,9 @@ type withMessage struct {
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause } 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) { func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb { switch verb {
case 'v': case 'v':

38
kuryr_cni/vendor/github.com/pkg/errors/go113.go generated vendored Normal file
View File

@ -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)
}

View File

@ -5,10 +5,13 @@ import (
"io" "io"
"path" "path"
"runtime" "runtime"
"strconv"
"strings" "strings"
) )
// Frame represents a program counter inside a stack frame. // 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 type Frame uintptr
// pc returns the program counter for this frame; // pc returns the program counter for this frame;
@ -37,6 +40,15 @@ func (f Frame) line() int {
return line 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. // Format formats the frame according to the fmt.Formatter interface.
// //
// %s source file // %s source file
@ -54,22 +66,16 @@ func (f Frame) Format(s fmt.State, verb rune) {
case 's': case 's':
switch { switch {
case s.Flag('+'): case s.Flag('+'):
pc := f.pc() io.WriteString(s, f.name())
fn := runtime.FuncForPC(pc) io.WriteString(s, "\n\t")
if fn == nil { io.WriteString(s, f.file())
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
default: default:
io.WriteString(s, path.Base(f.file())) io.WriteString(s, path.Base(f.file()))
} }
case 'd': case 'd':
fmt.Fprintf(s, "%d", f.line()) io.WriteString(s, strconv.Itoa(f.line()))
case 'n': case 'n':
name := runtime.FuncForPC(f.pc()).Name() io.WriteString(s, funcname(f.name()))
io.WriteString(s, funcname(name))
case 'v': case 'v':
f.Format(s, 's') f.Format(s, 's')
io.WriteString(s, ":") io.WriteString(s, ":")
@ -77,6 +83,16 @@ 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). // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame type StackTrace []Frame
@ -94,18 +110,32 @@ func (st StackTrace) Format(s fmt.State, verb rune) {
switch { switch {
case s.Flag('+'): case s.Flag('+'):
for _, f := range st { for _, f := range st {
fmt.Fprintf(s, "\n%+v", f) io.WriteString(s, "\n")
f.Format(s, verb)
} }
case s.Flag('#'): case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st)) fmt.Fprintf(s, "%#v", []Frame(st))
default: default:
fmt.Fprintf(s, "%v", []Frame(st)) st.formatSlice(s, verb)
} }
case 's': 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. // stack represents a stack of program counters.
type stack []uintptr type stack []uintptr