metadata.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package system // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/system"
  4. import (
  5. "context"
  6. "fmt"
  7. "net"
  8. "os"
  9. "runtime"
  10. "strings"
  11. "github.com/Showmax/go-fqdn"
  12. conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
  13. "go.opentelemetry.io/otel/attribute"
  14. "go.opentelemetry.io/otel/sdk/resource"
  15. "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/internal"
  16. )
  17. // nameInfoProvider abstracts domain name resolution so it can be swapped for
  18. // testing
  19. type nameInfoProvider struct {
  20. osHostname func() (string, error)
  21. lookupCNAME func(string) (string, error)
  22. lookupHost func(string) ([]string, error)
  23. lookupAddr func(string) ([]string, error)
  24. }
  25. // newNameInfoProvider creates a name info provider for production use, using
  26. // DNS to resolve domain names
  27. func newNameInfoProvider() nameInfoProvider {
  28. return nameInfoProvider{
  29. osHostname: os.Hostname,
  30. lookupCNAME: net.LookupCNAME,
  31. lookupHost: net.LookupHost,
  32. lookupAddr: net.LookupAddr,
  33. }
  34. }
  35. type Provider interface {
  36. // Hostname returns the OS hostname
  37. Hostname() (string, error)
  38. // FQDN returns the fully qualified domain name
  39. FQDN() (string, error)
  40. // OSDescription returns a human readable description of the OS.
  41. OSDescription(ctx context.Context) (string, error)
  42. // OSType returns the host operating system
  43. OSType() (string, error)
  44. // LookupCNAME returns the canonical name for the current host
  45. LookupCNAME() (string, error)
  46. // ReverseLookupHost does a reverse DNS query on the current host's IP address
  47. ReverseLookupHost() (string, error)
  48. // HostID returns Host Unique Identifier
  49. HostID(ctx context.Context) (string, error)
  50. // HostArch returns the host architecture
  51. HostArch() (string, error)
  52. }
  53. type systemMetadataProvider struct {
  54. nameInfoProvider
  55. newResource func(context.Context, ...resource.Option) (*resource.Resource, error)
  56. }
  57. func NewProvider() Provider {
  58. return systemMetadataProvider{nameInfoProvider: newNameInfoProvider(), newResource: resource.New}
  59. }
  60. func (systemMetadataProvider) OSType() (string, error) {
  61. return internal.GOOSToOSType(runtime.GOOS), nil
  62. }
  63. func (systemMetadataProvider) FQDN() (string, error) {
  64. return fqdn.FqdnHostname()
  65. }
  66. func (p systemMetadataProvider) Hostname() (string, error) {
  67. return p.nameInfoProvider.osHostname()
  68. }
  69. func (p systemMetadataProvider) LookupCNAME() (string, error) {
  70. hostname, err := p.Hostname()
  71. if err != nil {
  72. return "", fmt.Errorf("LookupCNAME failed to get hostname: %w", err)
  73. }
  74. cname, err := p.nameInfoProvider.lookupCNAME(hostname)
  75. if err != nil {
  76. return "", fmt.Errorf("LookupCNAME failed to get CNAME: %w", err)
  77. }
  78. return strings.TrimRight(cname, "."), nil
  79. }
  80. func (p systemMetadataProvider) ReverseLookupHost() (string, error) {
  81. hostname, err := p.Hostname()
  82. if err != nil {
  83. return "", fmt.Errorf("ReverseLookupHost failed to get hostname: %w", err)
  84. }
  85. return p.hostnameToDomainName(hostname)
  86. }
  87. func (p systemMetadataProvider) hostnameToDomainName(hostname string) (string, error) {
  88. ipAddresses, err := p.nameInfoProvider.lookupHost(hostname)
  89. if err != nil {
  90. return "", fmt.Errorf("hostnameToDomainName failed to convert hostname to IP addresses: %w", err)
  91. }
  92. return p.reverseLookup(ipAddresses)
  93. }
  94. func (p systemMetadataProvider) reverseLookup(ipAddresses []string) (string, error) {
  95. var err error
  96. for _, ip := range ipAddresses {
  97. var names []string
  98. names, err = p.nameInfoProvider.lookupAddr(ip)
  99. if err != nil {
  100. continue
  101. }
  102. return strings.TrimRight(names[0], "."), nil
  103. }
  104. return "", fmt.Errorf("reverseLookup failed to convert IP addresses to name: %w", err)
  105. }
  106. func (p systemMetadataProvider) fromOption(ctx context.Context, opt resource.Option, semconv string) (string, error) {
  107. res, err := p.newResource(ctx, opt)
  108. if err != nil {
  109. return "", fmt.Errorf("failed to obtain %q: %w", semconv, err)
  110. }
  111. iter := res.Iter()
  112. for iter.Next() {
  113. if iter.Attribute().Key == attribute.Key(semconv) {
  114. v := iter.Attribute().Value.Emit()
  115. if v == "" {
  116. return "", fmt.Errorf("empty %q", semconv)
  117. }
  118. return v, nil
  119. }
  120. }
  121. return "", fmt.Errorf("failed to obtain %q", semconv)
  122. }
  123. func (p systemMetadataProvider) HostID(ctx context.Context) (string, error) {
  124. return p.fromOption(ctx, resource.WithHostID(), conventions.AttributeHostID)
  125. }
  126. func (p systemMetadataProvider) OSDescription(ctx context.Context) (string, error) {
  127. return p.fromOption(ctx, resource.WithOSDescription(), conventions.AttributeOSDescription)
  128. }
  129. func (systemMetadataProvider) HostArch() (string, error) {
  130. return internal.GOARCHtoHostArch(runtime.GOARCH), nil
  131. }