diagnostic_utils.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package diagnostic
  2. import (
  3. "archive/zip"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "github.com/google/uuid"
  14. "github.com/rs/zerolog"
  15. )
  16. // CreateDiagnosticZipFile create a zip file with the contents from the all
  17. // files paths. The files will be written in the root of the zip file.
  18. // In case of an error occurs after whilst writing to the zip file
  19. // this will be removed.
  20. func CreateDiagnosticZipFile(base string, paths []string) (zipFileName string, err error) {
  21. // Create a zip file with all files from paths added to the root
  22. suffix := time.Now().Format(time.RFC3339)
  23. zipFileName = base + "-" + suffix + ".zip"
  24. zipFileName = strings.ReplaceAll(zipFileName, ":", "-")
  25. archive, cerr := os.Create(zipFileName)
  26. if cerr != nil {
  27. return "", fmt.Errorf("error creating file %s: %w", zipFileName, cerr)
  28. }
  29. archiveWriter := zip.NewWriter(archive)
  30. defer func() {
  31. archiveWriter.Close()
  32. archive.Close()
  33. if err != nil {
  34. os.Remove(zipFileName)
  35. }
  36. }()
  37. for _, file := range paths {
  38. if file == "" {
  39. continue
  40. }
  41. var handle *os.File
  42. handle, err = os.Open(file)
  43. if err != nil {
  44. return "", fmt.Errorf("error opening file %s: %w", zipFileName, err)
  45. }
  46. defer handle.Close()
  47. // Keep the base only to not create sub directories in the
  48. // zip file.
  49. var writer io.Writer
  50. writer, err = archiveWriter.Create(filepath.Base(file))
  51. if err != nil {
  52. return "", fmt.Errorf("error creating archive writer from %s: %w", file, err)
  53. }
  54. if _, err = io.Copy(writer, handle); err != nil {
  55. return "", fmt.Errorf("error copying file %s: %w", file, err)
  56. }
  57. }
  58. zipFileName = archive.Name()
  59. return zipFileName, nil
  60. }
  61. type AddressableTunnelState struct {
  62. *TunnelState
  63. URL *url.URL
  64. }
  65. func findMetricsServerPredicate(tunnelID, connectorID uuid.UUID) func(state *TunnelState) bool {
  66. if tunnelID != uuid.Nil && connectorID != uuid.Nil {
  67. return func(state *TunnelState) bool {
  68. return state.ConnectorID == connectorID && state.TunnelID == tunnelID
  69. }
  70. } else if tunnelID == uuid.Nil && connectorID != uuid.Nil {
  71. return func(state *TunnelState) bool {
  72. return state.ConnectorID == connectorID
  73. }
  74. } else if tunnelID != uuid.Nil && connectorID == uuid.Nil {
  75. return func(state *TunnelState) bool {
  76. return state.TunnelID == tunnelID
  77. }
  78. }
  79. return func(*TunnelState) bool {
  80. return true
  81. }
  82. }
  83. // The FindMetricsServer will try to find the metrics server url.
  84. // There are two possible error scenarios:
  85. // 1. No instance is found which will only return ErrMetricsServerNotFound
  86. // 2. Multiple instances are found which will return an array of state and ErrMultipleMetricsServerFound
  87. // In case of success, only the state for the instance is returned.
  88. func FindMetricsServer(
  89. log *zerolog.Logger,
  90. client *httpClient,
  91. addresses []string,
  92. ) (*AddressableTunnelState, []*AddressableTunnelState, error) {
  93. instances := make([]*AddressableTunnelState, 0)
  94. for _, address := range addresses {
  95. url, err := url.Parse("http://" + address)
  96. if err != nil {
  97. log.Debug().Err(err).Msgf("error parsing address %s", address)
  98. continue
  99. }
  100. client.SetBaseURL(url)
  101. state, err := client.GetTunnelState(context.Background())
  102. if err == nil {
  103. instances = append(instances, &AddressableTunnelState{state, url})
  104. } else {
  105. log.Debug().Err(err).Msgf("error getting tunnel state from address %s", address)
  106. }
  107. }
  108. if len(instances) == 0 {
  109. return nil, nil, ErrMetricsServerNotFound
  110. }
  111. if len(instances) == 1 {
  112. return instances[0], nil, nil
  113. }
  114. return nil, instances, ErrMultipleMetricsServerFound
  115. }
  116. // newFormattedEncoder return a JSON encoder with identation
  117. func newFormattedEncoder(w io.Writer) *json.Encoder {
  118. encoder := json.NewEncoder(w)
  119. encoder.SetIndent("", " ")
  120. return encoder
  121. }