1// Package hcn is a shim for the Host Compute Networking (HCN) service, which manages networking for Windows Server
2// containers and Hyper-V containers. Previous to RS5, HCN was referred to as Host Networking Service (HNS).
3package hcn
4
5import (
6	"errors"
7	"fmt"
8
9	"github.com/Microsoft/hcsshim/internal/hcs"
10	"github.com/Microsoft/hcsshim/internal/hcserror"
11	"github.com/Microsoft/hcsshim/internal/interop"
12	"github.com/sirupsen/logrus"
13)
14
15var (
16	errInvalidNetworkID      = errors.New("invalid network ID")
17	errInvalidEndpointID     = errors.New("invalid endpoint ID")
18	errInvalidNamespaceID    = errors.New("invalid namespace ID")
19	errInvalidLoadBalancerID = errors.New("invalid load balancer ID")
20	errInvalidRouteID        = errors.New("invalid route ID")
21)
22
23func checkForErrors(methodName string, hr error, resultBuffer *uint16) error {
24	errorFound := false
25
26	if hr != nil {
27		errorFound = true
28	}
29
30	result := ""
31	if resultBuffer != nil {
32		result = interop.ConvertAndFreeCoTaskMemString(resultBuffer)
33		if result != "" {
34			errorFound = true
35		}
36	}
37
38	if errorFound {
39		returnError := new(hr, methodName, result)
40		logrus.Debugf(returnError.Error()) // HCN errors logged for debugging.
41		return returnError
42	}
43
44	return nil
45}
46
47type ErrorCode uint32
48
49// For common errors, define the error as it is in windows, so we can quickly determine it later
50const (
51	ERROR_NOT_FOUND                     = 0x490
52	HCN_E_PORT_ALREADY_EXISTS ErrorCode = 0x803b0013
53)
54
55type HcnError struct {
56	*hcserror.HcsError
57	code ErrorCode
58}
59
60func (e *HcnError) Error() string {
61	return e.HcsError.Error()
62}
63
64func CheckErrorWithCode(err error, code ErrorCode) bool {
65	hcnError, ok := err.(*HcnError)
66	if ok {
67		return hcnError.code == code
68	}
69	return false
70}
71
72func IsElementNotFoundError(err error) bool {
73	return CheckErrorWithCode(err, ERROR_NOT_FOUND)
74}
75
76func IsPortAlreadyExistsError(err error) bool {
77	return CheckErrorWithCode(err, HCN_E_PORT_ALREADY_EXISTS)
78}
79
80func new(hr error, title string, rest string) error {
81	err := &HcnError{}
82	hcsError := hcserror.New(hr, title, rest)
83	err.HcsError = hcsError.(*hcserror.HcsError)
84	err.code = ErrorCode(hcserror.Win32FromError(hr))
85	return err
86}
87
88//
89// Note that the below errors are not errors returned by hcn itself
90// we wish to seperate them as they are shim usage error
91//
92
93// NetworkNotFoundError results from a failed seach for a network by Id or Name
94type NetworkNotFoundError struct {
95	NetworkName string
96	NetworkID   string
97}
98
99func (e NetworkNotFoundError) Error() string {
100	if e.NetworkName != "" {
101		return fmt.Sprintf("Network name %q not found", e.NetworkName)
102	}
103	return fmt.Sprintf("Network ID %q not found", e.NetworkID)
104}
105
106// EndpointNotFoundError results from a failed seach for an endpoint by Id or Name
107type EndpointNotFoundError struct {
108	EndpointName string
109	EndpointID   string
110}
111
112func (e EndpointNotFoundError) Error() string {
113	if e.EndpointName != "" {
114		return fmt.Sprintf("Endpoint name %q not found", e.EndpointName)
115	}
116	return fmt.Sprintf("Endpoint ID %q not found", e.EndpointID)
117}
118
119// NamespaceNotFoundError results from a failed seach for a namsepace by Id
120type NamespaceNotFoundError struct {
121	NamespaceID string
122}
123
124func (e NamespaceNotFoundError) Error() string {
125	return fmt.Sprintf("Namespace ID %q not found", e.NamespaceID)
126}
127
128// LoadBalancerNotFoundError results from a failed seach for a loadbalancer by Id
129type LoadBalancerNotFoundError struct {
130	LoadBalancerId string
131}
132
133func (e LoadBalancerNotFoundError) Error() string {
134	return fmt.Sprintf("LoadBalancer %q not found", e.LoadBalancerId)
135}
136
137// RouteNotFoundError results from a failed seach for a route by Id
138type RouteNotFoundError struct {
139	RouteId string
140}
141
142func (e RouteNotFoundError) Error() string {
143	return fmt.Sprintf("SDN Route %q not found", e.RouteId)
144}
145
146// IsNotFoundError returns a boolean indicating whether the error was caused by
147// a resource not being found.
148func IsNotFoundError(err error) bool {
149	switch pe := err.(type) {
150	case NetworkNotFoundError:
151		return true
152	case EndpointNotFoundError:
153		return true
154	case NamespaceNotFoundError:
155		return true
156	case LoadBalancerNotFoundError:
157		return true
158	case RouteNotFoundError:
159		return true
160	case *hcserror.HcsError:
161		return pe.Err == hcs.ErrElementNotFound
162	}
163	return false
164}
165