mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
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>
389 lines
12 KiB
Go
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)
|
|
}
|