metadata.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  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 cloudstack
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "io/ioutil"
  19. "net"
  20. "net/http"
  21. "github.com/d2g/dhcp4"
  22. "k8s.io/api/core/v1"
  23. "k8s.io/apimachinery/pkg/types"
  24. cloudprovider "k8s.io/cloud-provider"
  25. "k8s.io/klog"
  26. )
  27. var _ cloudprovider.Instances = (*metadata)(nil)
  28. var _ cloudprovider.Zones = (*metadata)(nil)
  29. type metadata struct {
  30. dhcpServer string
  31. zone string
  32. }
  33. type metadataType string
  34. const (
  35. metadataTypeHostname metadataType = "local-hostname"
  36. metadataTypeExternalIP metadataType = "public-ipv4"
  37. metadataTypeInternalIP metadataType = "local-ipv4"
  38. metadataTypeInstanceID metadataType = "instance-id"
  39. metadataTypeInstanceType metadataType = "service-offering"
  40. metadataTypeZone metadataType = "availability-zone"
  41. )
  42. // NodeAddresses returns the addresses of the specified instance.
  43. func (m *metadata) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error) {
  44. externalIP, err := m.get(metadataTypeExternalIP)
  45. if err != nil {
  46. return nil, fmt.Errorf("could not get external IP: %v", err)
  47. }
  48. internalIP, err := m.get(metadataTypeInternalIP)
  49. if err != nil {
  50. return nil, fmt.Errorf("could not get internal IP: %v", err)
  51. }
  52. addresses := []v1.NodeAddress{
  53. {Type: v1.NodeExternalIP, Address: externalIP},
  54. {Type: v1.NodeInternalIP, Address: internalIP},
  55. }
  56. hostname, err := m.get(metadataTypeHostname)
  57. if err != nil {
  58. return nil, fmt.Errorf("could not get hostname: %v", err)
  59. }
  60. if hostname != "" {
  61. addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
  62. }
  63. return addresses, nil
  64. }
  65. // NodeAddressesByProviderID returns the addresses of the specified instance.
  66. func (m *metadata) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
  67. return nil, errors.New("NodeAddressesByProviderID not implemented")
  68. }
  69. // InstanceID returns the cloud provider ID of the specified instance.
  70. func (m *metadata) InstanceID(ctx context.Context, name types.NodeName) (string, error) {
  71. instanceID, err := m.get(metadataTypeInstanceID)
  72. if err != nil {
  73. return "", fmt.Errorf("could not get instance ID: %v", err)
  74. }
  75. zone, err := m.get(metadataTypeZone)
  76. if err != nil {
  77. return "", fmt.Errorf("could not get zone: %v", err)
  78. }
  79. return "/" + zone + "/" + instanceID, nil
  80. }
  81. // InstanceType returns the type of the specified instance.
  82. func (m *metadata) InstanceType(ctx context.Context, name types.NodeName) (string, error) {
  83. instanceType, err := m.get(metadataTypeInstanceType)
  84. if err != nil {
  85. return "", fmt.Errorf("could not get instance type: %v", err)
  86. }
  87. return instanceType, nil
  88. }
  89. // InstanceTypeByProviderID returns the type of the specified instance.
  90. func (m *metadata) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) {
  91. return "", errors.New("InstanceTypeByProviderID not implemented")
  92. }
  93. // AddSSHKeyToAllInstances is currently not implemented.
  94. func (m *metadata) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
  95. return cloudprovider.NotImplemented
  96. }
  97. // CurrentNodeName returns the name of the node we are currently running on.
  98. func (m *metadata) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) {
  99. return types.NodeName(hostname), nil
  100. }
  101. // InstanceExistsByProviderID returns if the instance still exists.
  102. func (m *metadata) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
  103. return false, errors.New("InstanceExistsByProviderID not implemented")
  104. }
  105. // InstanceShutdownByProviderID returns if the instance is shutdown.
  106. func (m *metadata) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
  107. return false, cloudprovider.NotImplemented
  108. }
  109. // GetZone returns the Zone containing the region that the program is running in.
  110. func (m *metadata) GetZone(ctx context.Context) (cloudprovider.Zone, error) {
  111. zone := cloudprovider.Zone{}
  112. if m.zone == "" {
  113. zoneName, err := m.get(metadataTypeZone)
  114. if err != nil {
  115. return zone, fmt.Errorf("could not get zone: %v", err)
  116. }
  117. m.zone = zoneName
  118. }
  119. klog.V(2).Infof("Current zone is %v", zone)
  120. zone.FailureDomain = m.zone
  121. zone.Region = m.zone
  122. return zone, nil
  123. }
  124. // GetZoneByProviderID returns the Zone, found by using the provider ID.
  125. func (m *metadata) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) {
  126. return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented")
  127. }
  128. // GetZoneByNodeName returns the Zone, found by using the node name.
  129. func (m *metadata) GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (cloudprovider.Zone, error) {
  130. return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not implemented")
  131. }
  132. func (m *metadata) get(mdType metadataType) (string, error) {
  133. url := fmt.Sprintf("http://%s/latest/meta-data/%s", m.dhcpServer, mdType)
  134. resp, err := http.Get(url)
  135. if err != nil {
  136. return "", fmt.Errorf("error reading metadata: %v", err)
  137. }
  138. defer resp.Body.Close()
  139. if resp.StatusCode != http.StatusOK {
  140. return "", fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode)
  141. }
  142. data, err := ioutil.ReadAll(resp.Body)
  143. if err != nil {
  144. return "", fmt.Errorf("error reading response body: %d", resp.StatusCode)
  145. }
  146. return string(data), nil
  147. }
  148. func findDHCPServer() (string, error) {
  149. nics, err := net.Interfaces()
  150. if err != nil {
  151. return "", fmt.Errorf("could not get interfaces: %v", err)
  152. }
  153. for _, nic := range nics {
  154. if nic.Flags&net.FlagUp == 1 && nic.Flags&net.FlagLoopback == 0 && nic.Flags&net.FlagPointToPoint == 0 {
  155. addrs, err := nic.Addrs()
  156. if err != nil {
  157. return "", fmt.Errorf("error reading IP addresses from interface %v: %v", nic.Name, err)
  158. }
  159. if addrs != nil {
  160. client, err := newDHCPClient(&nic)
  161. if err != nil {
  162. return "", fmt.Errorf("error creating new DHCP client: %v", err)
  163. }
  164. discoverPacket, err := client.SendDiscoverPacket()
  165. if err != nil {
  166. return "", fmt.Errorf("error sending DHCP discover package: %v", err)
  167. }
  168. offerPacket, err := client.GetOffer(&discoverPacket)
  169. if err != nil {
  170. return "", fmt.Errorf("error receiving DHCP offer package: %v", err)
  171. }
  172. offerPacketOptions := offerPacket.ParseOptions()
  173. if ipaddr, ok := offerPacketOptions[dhcp4.OptionServerIdentifier]; ok {
  174. return net.IP(ipaddr).String(), nil
  175. }
  176. }
  177. }
  178. }
  179. return "", errors.New("no server found")
  180. }