diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go new file mode 100644 index 00000000..0fb302ee --- /dev/null +++ b/pkg/errors/errors.go @@ -0,0 +1,38 @@ +// Copyright 2020 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 errors + +import "fmt" + + +// Annotate is used to add extra context to an existing error. The return will be +// a new error which carries error message from both context message and existing error. +func Annotate(err error, message string) error { + if err == nil { + return nil + } + + return fmt.Errorf("%s: %v", message, err) +} + +// Annotatef is used to add extra context with args to an existing error. The return will be +// a new error which carries error message from both context message and existing error. +func Annotatef(err error, message string, args ...interface{}) error { + if err == nil { + return nil + } + + return fmt.Errorf("%s: %v", fmt.Sprintf(message, args...), err) +} diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go new file mode 100644 index 00000000..cd39044c --- /dev/null +++ b/pkg/errors/errors_test.go @@ -0,0 +1,96 @@ +// Copyright 2020 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 errors + +import ( + "errors" + "reflect" + "testing" +) + +func TestAnnotate(t *testing.T) { + tests := []struct { + name string + existingErr error + contextMessage string + expectedErr error + }{ + { + "nil error", + nil, + "context", + nil, + }, + { + "normal case", + errors.New("existing error"), + "context", + errors.New("context: existing error"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage), test.expectedErr) { + t.Errorf("test case %s fails", test.name) + return + } + }) + } +} + +func TestAnnotatef(t *testing.T) { + tests := []struct { + name string + existingErr error + contextMessage string + contextArgs []interface{} + expectedErr error + }{ + { + "nil error", + nil, + "context", + nil, + nil, + }, + { + "normal case", + errors.New("existing error"), + "context", + nil, + errors.New("context: existing error"), + }, + { + "normal case with args", + errors.New("existing error"), + "context %s %d", + []interface{}{ + "arg", + 100, + }, + errors.New("context arg 100: existing error"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage, test.contextArgs...), test.expectedErr) { + t.Errorf("test case %s fails", test.name) + return + } + }) + } +} diff --git a/pkg/hns/endpoint_windows.go b/pkg/hns/endpoint_windows.go index cd3025e1..777f07b4 100644 --- a/pkg/hns/endpoint_windows.go +++ b/pkg/hns/endpoint_windows.go @@ -21,9 +21,10 @@ import ( "github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim/hcn" + "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" - "github.com/juju/errors" + "github.com/containernetworking/plugins/pkg/errors" ) const ( @@ -64,14 +65,14 @@ func GenerateHnsEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcsshim.HNSEndpoint // run the IPAM plugin and get back the config to apply hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName) if err != nil && !hcsshim.IsNotExist(err) { - return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName) + return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName) } if hnsEndpoint != nil { if hnsEndpoint.VirtualNetwork != epInfo.NetworkId { _, err = hnsEndpoint.Delete() if err != nil { - return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName) + return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName) } hnsEndpoint = nil } @@ -98,7 +99,7 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp // run the IPAM plugin and get back the config to apply hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName) if err != nil && !hcn.IsNotFoundError(err) { - return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName) + return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName) } if hcnEndpoint != nil { @@ -108,12 +109,10 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp if !strings.EqualFold(hcnEndpoint.HostComputeNetwork, epInfo.NetworkId) { err = hcnEndpoint.Delete() if err != nil { - return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName) - hcnEndpoint = nil - + return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName) } } else { - return nil, fmt.Errorf("Endpoint \"%v\" already exits", epInfo.EndpointName) + return nil, fmt.Errorf("endpoint %q already exits", epInfo.EndpointName) } } @@ -270,7 +269,7 @@ func AddHcnEndpoint(epName string, expectedNetworkId string, namespace string, if err != nil { return nil, errors.Annotatef(err, "failed to Remove Endpoint after AddNamespaceEndpoint failure") } - return nil, errors.Annotatef(err, "Failed to Add endpoint to namespace") + return nil, errors.Annotate(err, "failed to Add endpoint to namespace") } return hcnEndpoint, nil diff --git a/plugins/main/windows/win-bridge/win-bridge_windows.go b/plugins/main/windows/win-bridge/win-bridge_windows.go index c1c68a17..5d7ce835 100644 --- a/plugins/main/windows/win-bridge/win-bridge_windows.go +++ b/plugins/main/windows/win-bridge/win-bridge_windows.go @@ -22,13 +22,13 @@ import ( "github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim/hcn" - "github.com/juju/errors" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/errors" "github.com/containernetworking/plugins/pkg/hns" "github.com/containernetworking/plugins/pkg/ipam" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" @@ -72,7 +72,7 @@ func ProcessEndpointArgs(args *skel.CmdArgs, n *NetConf) (*hns.EndpointInfo, err return nil, errors.Annotatef(err, "error while NewResultFromResult") } else { if len(result.IPs) == 0 { - return nil, errors.New("IPAM plugin return is missing IP config") + return nil, fmt.Errorf("IPAM plugin return is missing IP config") } epInfo.IpAddress = result.IPs[0].Address.IP epInfo.Gateway = result.IPs[0].Address.IP.Mask(result.IPs[0].Address.Mask) @@ -194,7 +194,7 @@ func cmdAdd(args *skel.CmdArgs) error { } if result == nil { - return errors.New("result for ADD not populated correctly") + return fmt.Errorf("result for ADD not populated correctly") } return types.PrintResult(result, cniVersion) } diff --git a/plugins/main/windows/win-overlay/win-overlay_windows.go b/plugins/main/windows/win-overlay/win-overlay_windows.go index 2674c8b5..c133e921 100644 --- a/plugins/main/windows/win-overlay/win-overlay_windows.go +++ b/plugins/main/windows/win-overlay/win-overlay_windows.go @@ -21,13 +21,13 @@ import ( "strings" "github.com/Microsoft/hcsshim" - "github.com/juju/errors" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" + "github.com/containernetworking/plugins/pkg/errors" "github.com/containernetworking/plugins/pkg/hns" "github.com/containernetworking/plugins/pkg/ipam" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" @@ -100,12 +100,12 @@ func cmdAdd(args *skel.CmdArgs) error { } if len(result.IPs) == 0 { - return nil, errors.New("IPAM plugin return is missing IP config") + return nil, fmt.Errorf("IPAM plugin return is missing IP config") } ipAddr := result.IPs[0].Address.IP.To4() if ipAddr == nil { - return nil, errors.New("win-overlay doesn't support IPv6 now") + return nil, fmt.Errorf("win-overlay doesn't support IPv6 now") } // conjure a MAC based on the IP for Overlay