kubernetes/vendor/github.com/Microsoft/hcsshim/hcn/hcnendpoint.go
Daniel Canter 3eef755b3b vendor: bump hcsshim to v0.8.22
This tag of hcsshim brings in a couple welcome features/improvements. One being
exposing a way to query for hns endpoint statistics (Packets received/sent etc.).
This tag also contains some optimizations for querying whether a certain HCN feature
is supported, which is a common workflow in kube-proxy on Windows. The first result
from querying HCN is now cached so further calls can skip the hcn query as well as the
version range parsing that was performed. This also gets rid of some redundant logs
that used to hit everytime the version range parsing occurred.

The Go-winio dep bump, and all of the ctrd deps are transitive only. Nothing new is needed/intended
to be used.

Signed-off-by: Daniel Canter <dcanter@microsoft.com>
2021-09-10 00:54:24 -07:00

389 lines
12 KiB
Go

package hcn
import (
"encoding/json"
"errors"
"github.com/Microsoft/go-winio/pkg/guid"
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/sirupsen/logrus"
)
// IpConfig is assoicated with an endpoint
type IpConfig struct {
IpAddress string `json:",omitempty"`
PrefixLength uint8 `json:",omitempty"`
}
// EndpointFlags are special settings on an endpoint.
type EndpointFlags uint32
var (
// EndpointFlagsNone is the default.
EndpointFlagsNone EndpointFlags
// EndpointFlagsRemoteEndpoint means that an endpoint is on another host.
EndpointFlagsRemoteEndpoint EndpointFlags = 1
)
// HostComputeEndpoint represents a network endpoint
type HostComputeEndpoint struct {
Id string `json:"ID,omitempty"`
Name string `json:",omitempty"`
HostComputeNetwork string `json:",omitempty"` // GUID
HostComputeNamespace string `json:",omitempty"` // GUID
Policies []EndpointPolicy `json:",omitempty"`
IpConfigurations []IpConfig `json:",omitempty"`
Dns Dns `json:",omitempty"`
Routes []Route `json:",omitempty"`
MacAddress string `json:",omitempty"`
Flags EndpointFlags `json:",omitempty"`
Health Health `json:",omitempty"`
SchemaVersion SchemaVersion `json:",omitempty"`
}
// EndpointResourceType are the two different Endpoint settings resources.
type EndpointResourceType string
var (
// EndpointResourceTypePolicy is for Endpoint Policies. Ex: ACL, NAT
EndpointResourceTypePolicy EndpointResourceType = "Policy"
// EndpointResourceTypePort is for Endpoint Port settings.
EndpointResourceTypePort EndpointResourceType = "Port"
)
// ModifyEndpointSettingRequest is the structure used to send request to modify an endpoint.
// Used to update policy/port on an endpoint.
type ModifyEndpointSettingRequest struct {
ResourceType EndpointResourceType `json:",omitempty"` // Policy, Port
RequestType RequestType `json:",omitempty"` // Add, Remove, Update, Refresh
Settings json.RawMessage `json:",omitempty"`
}
// VmEndpointRequest creates a switch port with identifier `PortId`.
type VmEndpointRequest struct {
PortId guid.GUID `json:",omitempty"`
VirtualNicName string `json:",omitempty"`
VirtualMachineId guid.GUID `json:",omitempty"`
}
type PolicyEndpointRequest struct {
Policies []EndpointPolicy `json:",omitempty"`
}
func getEndpoint(endpointGuid guid.GUID, query string) (*HostComputeEndpoint, error) {
// Open endpoint.
var (
endpointHandle hcnEndpoint
resultBuffer *uint16
propertiesBuffer *uint16
)
hr := hcnOpenEndpoint(&endpointGuid, &endpointHandle, &resultBuffer)
if err := checkForErrors("hcnOpenEndpoint", hr, resultBuffer); err != nil {
return nil, err
}
// Query endpoint.
hr = hcnQueryEndpointProperties(endpointHandle, query, &propertiesBuffer, &resultBuffer)
if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil {
return nil, err
}
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
// Close endpoint.
hr = hcnCloseEndpoint(endpointHandle)
if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil {
return nil, err
}
// Convert output to HostComputeEndpoint
var outputEndpoint HostComputeEndpoint
if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil {
return nil, err
}
return &outputEndpoint, nil
}
func enumerateEndpoints(query string) ([]HostComputeEndpoint, error) {
// Enumerate all Endpoint Guids
var (
resultBuffer *uint16
endpointBuffer *uint16
)
hr := hcnEnumerateEndpoints(query, &endpointBuffer, &resultBuffer)
if err := checkForErrors("hcnEnumerateEndpoints", hr, resultBuffer); err != nil {
return nil, err
}
endpoints := interop.ConvertAndFreeCoTaskMemString(endpointBuffer)
var endpointIds []guid.GUID
err := json.Unmarshal([]byte(endpoints), &endpointIds)
if err != nil {
return nil, err
}
var outputEndpoints []HostComputeEndpoint
for _, endpointGuid := range endpointIds {
endpoint, err := getEndpoint(endpointGuid, query)
if err != nil {
return nil, err
}
outputEndpoints = append(outputEndpoints, *endpoint)
}
return outputEndpoints, nil
}
func createEndpoint(networkId string, endpointSettings string) (*HostComputeEndpoint, error) {
networkGuid, err := guid.FromString(networkId)
if err != nil {
return nil, errInvalidNetworkID
}
// Open network.
var networkHandle hcnNetwork
var resultBuffer *uint16
hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer)
if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil {
return nil, err
}
// Create endpoint.
endpointId := guid.GUID{}
var endpointHandle hcnEndpoint
hr = hcnCreateEndpoint(networkHandle, &endpointId, endpointSettings, &endpointHandle, &resultBuffer)
if err := checkForErrors("hcnCreateEndpoint", hr, resultBuffer); err != nil {
return nil, err
}
// Query endpoint.
hcnQuery := defaultQuery()
query, err := json.Marshal(hcnQuery)
if err != nil {
return nil, err
}
var propertiesBuffer *uint16
hr = hcnQueryEndpointProperties(endpointHandle, string(query), &propertiesBuffer, &resultBuffer)
if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil {
return nil, err
}
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
// Close endpoint.
hr = hcnCloseEndpoint(endpointHandle)
if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil {
return nil, err
}
// Close network.
hr = hcnCloseNetwork(networkHandle)
if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
return nil, err
}
// Convert output to HostComputeEndpoint
var outputEndpoint HostComputeEndpoint
if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil {
return nil, err
}
return &outputEndpoint, nil
}
func modifyEndpoint(endpointId string, settings string) (*HostComputeEndpoint, error) {
endpointGuid, err := guid.FromString(endpointId)
if err != nil {
return nil, errInvalidEndpointID
}
// Open endpoint
var (
endpointHandle hcnEndpoint
resultBuffer *uint16
propertiesBuffer *uint16
)
hr := hcnOpenEndpoint(&endpointGuid, &endpointHandle, &resultBuffer)
if err := checkForErrors("hcnOpenEndpoint", hr, resultBuffer); err != nil {
return nil, err
}
// Modify endpoint
hr = hcnModifyEndpoint(endpointHandle, settings, &resultBuffer)
if err := checkForErrors("hcnModifyEndpoint", hr, resultBuffer); err != nil {
return nil, err
}
// Query endpoint.
hcnQuery := defaultQuery()
query, err := json.Marshal(hcnQuery)
if err != nil {
return nil, err
}
hr = hcnQueryEndpointProperties(endpointHandle, string(query), &propertiesBuffer, &resultBuffer)
if err := checkForErrors("hcnQueryEndpointProperties", hr, resultBuffer); err != nil {
return nil, err
}
properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
// Close endpoint.
hr = hcnCloseEndpoint(endpointHandle)
if err := checkForErrors("hcnCloseEndpoint", hr, nil); err != nil {
return nil, err
}
// Convert output to HostComputeEndpoint
var outputEndpoint HostComputeEndpoint
if err := json.Unmarshal([]byte(properties), &outputEndpoint); err != nil {
return nil, err
}
return &outputEndpoint, nil
}
func deleteEndpoint(endpointId string) error {
endpointGuid, err := guid.FromString(endpointId)
if err != nil {
return errInvalidEndpointID
}
var resultBuffer *uint16
hr := hcnDeleteEndpoint(&endpointGuid, &resultBuffer)
if err := checkForErrors("hcnDeleteEndpoint", hr, resultBuffer); err != nil {
return err
}
return nil
}
// ListEndpoints makes a call to list all available endpoints.
func ListEndpoints() ([]HostComputeEndpoint, error) {
hcnQuery := defaultQuery()
endpoints, err := ListEndpointsQuery(hcnQuery)
if err != nil {
return nil, err
}
return endpoints, nil
}
// ListEndpointsQuery makes a call to query the list of available endpoints.
func ListEndpointsQuery(query HostComputeQuery) ([]HostComputeEndpoint, error) {
queryJson, err := json.Marshal(query)
if err != nil {
return nil, err
}
endpoints, err := enumerateEndpoints(string(queryJson))
if err != nil {
return nil, err
}
return endpoints, nil
}
// ListEndpointsOfNetwork queries the list of endpoints on a network.
func ListEndpointsOfNetwork(networkId string) ([]HostComputeEndpoint, error) {
hcnQuery := defaultQuery()
// TODO: Once query can convert schema, change to {HostComputeNetwork:networkId}
mapA := map[string]string{"VirtualNetwork": networkId}
filter, err := json.Marshal(mapA)
if err != nil {
return nil, err
}
hcnQuery.Filter = string(filter)
return ListEndpointsQuery(hcnQuery)
}
// GetEndpointByID returns an endpoint specified by Id
func GetEndpointByID(endpointId string) (*HostComputeEndpoint, error) {
hcnQuery := defaultQuery()
mapA := map[string]string{"ID": endpointId}
filter, err := json.Marshal(mapA)
if err != nil {
return nil, err
}
hcnQuery.Filter = string(filter)
endpoints, err := ListEndpointsQuery(hcnQuery)
if err != nil {
return nil, err
}
if len(endpoints) == 0 {
return nil, EndpointNotFoundError{EndpointID: endpointId}
}
return &endpoints[0], err
}
// GetEndpointByName returns an endpoint specified by Name
func GetEndpointByName(endpointName string) (*HostComputeEndpoint, error) {
hcnQuery := defaultQuery()
mapA := map[string]string{"Name": endpointName}
filter, err := json.Marshal(mapA)
if err != nil {
return nil, err
}
hcnQuery.Filter = string(filter)
endpoints, err := ListEndpointsQuery(hcnQuery)
if err != nil {
return nil, err
}
if len(endpoints) == 0 {
return nil, EndpointNotFoundError{EndpointName: endpointName}
}
return &endpoints[0], err
}
// Create Endpoint.
func (endpoint *HostComputeEndpoint) Create() (*HostComputeEndpoint, error) {
logrus.Debugf("hcn::HostComputeEndpoint::Create id=%s", endpoint.Id)
if endpoint.HostComputeNamespace != "" {
return nil, errors.New("endpoint create error, endpoint json HostComputeNamespace is read only and should not be set")
}
jsonString, err := json.Marshal(endpoint)
if err != nil {
return nil, err
}
logrus.Debugf("hcn::HostComputeEndpoint::Create JSON: %s", jsonString)
endpoint, hcnErr := createEndpoint(endpoint.HostComputeNetwork, string(jsonString))
if hcnErr != nil {
return nil, hcnErr
}
return endpoint, nil
}
// Delete Endpoint.
func (endpoint *HostComputeEndpoint) Delete() error {
logrus.Debugf("hcn::HostComputeEndpoint::Delete id=%s", endpoint.Id)
if err := deleteEndpoint(endpoint.Id); err != nil {
return err
}
return nil
}
// ModifyEndpointSettings updates the Port/Policy of an Endpoint.
func ModifyEndpointSettings(endpointId string, request *ModifyEndpointSettingRequest) error {
logrus.Debugf("hcn::HostComputeEndpoint::ModifyEndpointSettings id=%s", endpointId)
endpointSettingsRequest, err := json.Marshal(request)
if err != nil {
return err
}
_, err = modifyEndpoint(endpointId, string(endpointSettingsRequest))
if err != nil {
return err
}
return nil
}
// ApplyPolicy applies a Policy (ex: ACL) on the Endpoint.
func (endpoint *HostComputeEndpoint) ApplyPolicy(requestType RequestType, endpointPolicy PolicyEndpointRequest) error {
logrus.Debugf("hcn::HostComputeEndpoint::ApplyPolicy id=%s", endpoint.Id)
settingsJson, err := json.Marshal(endpointPolicy)
if err != nil {
return err
}
requestMessage := &ModifyEndpointSettingRequest{
ResourceType: EndpointResourceTypePolicy,
RequestType: requestType,
Settings: settingsJson,
}
return ModifyEndpointSettings(endpoint.Id, requestMessage)
}
// NamespaceAttach modifies a Namespace to add an endpoint.
func (endpoint *HostComputeEndpoint) NamespaceAttach(namespaceId string) error {
return AddNamespaceEndpoint(namespaceId, endpoint.Id)
}
// NamespaceDetach modifies a Namespace to remove an endpoint.
func (endpoint *HostComputeEndpoint) NamespaceDetach(namespaceId string) error {
return RemoveNamespaceEndpoint(namespaceId, endpoint.Id)
}