helpers.go 4.3 KB

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