github.com/vmware/govmomi@v0.37.2/internal/helpers.go (about)

     1  /*
     2  Copyright (c) 2020-2023 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package internal
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"path"
    27  
    28  	"github.com/vmware/govmomi/vim25"
    29  	"github.com/vmware/govmomi/vim25/mo"
    30  	"github.com/vmware/govmomi/vim25/soap"
    31  	"github.com/vmware/govmomi/vim25/types"
    32  )
    33  
    34  const (
    35  	vCenterHostGatewaySocket    = "/var/run/envoy-hgw/hgw-pipe"
    36  	vCenterHostGatewaySocketEnv = "VCENTER_ENVOY_HOST_GATEWAY"
    37  )
    38  
    39  // InventoryPath composed of entities by Name
    40  func InventoryPath(entities []mo.ManagedEntity) string {
    41  	val := "/"
    42  
    43  	for _, entity := range entities {
    44  		// Skip root folder in building inventory path.
    45  		if entity.Parent == nil {
    46  			continue
    47  		}
    48  		val = path.Join(val, entity.Name)
    49  	}
    50  
    51  	return val
    52  }
    53  
    54  func HostSystemManagementIPs(config []types.VirtualNicManagerNetConfig) []net.IP {
    55  	var ips []net.IP
    56  
    57  	for _, nc := range config {
    58  		if nc.NicType != string(types.HostVirtualNicManagerNicTypeManagement) {
    59  			continue
    60  		}
    61  		for ix := range nc.CandidateVnic {
    62  			for _, selectedVnicKey := range nc.SelectedVnic {
    63  				if nc.CandidateVnic[ix].Key != selectedVnicKey {
    64  					continue
    65  				}
    66  				ip := net.ParseIP(nc.CandidateVnic[ix].Spec.Ip.IpAddress)
    67  				if ip != nil {
    68  					ips = append(ips, ip)
    69  				}
    70  			}
    71  		}
    72  	}
    73  
    74  	return ips
    75  }
    76  
    77  // UsingEnvoySidecar determines if the given *vim25.Client is using vCenter's
    78  // local Envoy sidecar (as opposed to using the HTTPS port.)
    79  // Returns a boolean indicating whether to use the sidecar or not.
    80  func UsingEnvoySidecar(c *vim25.Client) bool {
    81  	envoySidecarPort := os.Getenv("GOVMOMI_ENVOY_SIDECAR_PORT")
    82  	if envoySidecarPort == "" {
    83  		envoySidecarPort = "1080"
    84  	}
    85  	envoySidecarHost := os.Getenv("GOVMOMI_ENVOY_SIDECAR_HOST")
    86  	if envoySidecarHost == "" {
    87  		envoySidecarHost = "localhost"
    88  	}
    89  	return c.URL().Hostname() == envoySidecarHost && c.URL().Scheme == "http" && c.URL().Port() == envoySidecarPort
    90  }
    91  
    92  // ClientWithEnvoyHostGateway clones the provided soap.Client and returns a new
    93  // one that uses a Unix socket to leverage vCenter's local Envoy host
    94  // gateway.
    95  // This should be used to construct clients that talk to ESX.
    96  // This method returns a new *vim25.Client and does not modify the original input.
    97  // This client disables HTTP keep alives and is intended for a single round
    98  // trip. (eg. guest file transfer, datastore file transfer)
    99  func ClientWithEnvoyHostGateway(vc *vim25.Client) *vim25.Client {
   100  	// Override the vim client with a new one that wraps a Unix socket transport.
   101  	// Using HTTP here so secure means nothing.
   102  	sc := soap.NewClient(vc.URL(), true)
   103  	// Clone the underlying HTTP transport, only replacing the dialer logic.
   104  	transport := sc.DefaultTransport().Clone()
   105  	hostGatewaySocketPath := os.Getenv(vCenterHostGatewaySocketEnv)
   106  	if hostGatewaySocketPath == "" {
   107  		hostGatewaySocketPath = vCenterHostGatewaySocket
   108  	}
   109  	transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
   110  		return net.Dial("unix", hostGatewaySocketPath)
   111  	}
   112  	// We use this client for a single request, so we don't require keepalives.
   113  	transport.DisableKeepAlives = true
   114  	sc.Client = http.Client{
   115  		Transport: transport,
   116  	}
   117  	newVC := &vim25.Client{
   118  		Client: sc,
   119  	}
   120  	return newVC
   121  }
   122  
   123  // HostGatewayTransferURL rewrites the provided URL to be suitable for use
   124  // with the Envoy host gateway on vCenter.
   125  // It returns a copy of the provided URL with the host, scheme rewritten as needed.
   126  // Receivers of such URLs must typically also use ClientWithEnvoyHostGateway to
   127  // use the appropriate http.Transport to be able to make use of the host
   128  // gateway.
   129  // nil input yields an uninitialized struct.
   130  func HostGatewayTransferURL(u *url.URL, hostMoref types.ManagedObjectReference) *url.URL {
   131  	if u == nil {
   132  		return &url.URL{}
   133  	}
   134  	// Make a copy of the provided URL.
   135  	turl := *u
   136  	turl.Host = "localhost"
   137  	turl.Scheme = "http"
   138  	oldPath := turl.Path
   139  	turl.Path = fmt.Sprintf("/hgw/%s%s", hostMoref.Value, oldPath)
   140  	return &turl
   141  }