run.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. Copyright 2018 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 clonerefs
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "os/exec"
  20. "path/filepath"
  21. "strings"
  22. "sync"
  23. "github.com/sirupsen/logrus"
  24. "k8s.io/test-infra/prow/kube"
  25. "k8s.io/test-infra/prow/pod-utils/clone"
  26. )
  27. var cloneFunc = clone.Run
  28. // Run clones the configured refs
  29. func (o Options) Run() error {
  30. var env []string
  31. if len(o.KeyFiles) > 0 {
  32. var err error
  33. env, err = addSSHKeys(o.KeyFiles)
  34. if err != nil {
  35. logrus.WithError(err).Error("Failed to add SSH keys.")
  36. // Continue on error. Clones will fail with an appropriate error message
  37. // that initupload can consume whereas quitting without writing the clone
  38. // record log is silent and results in an errored prow job instead of a
  39. // failed one.
  40. }
  41. }
  42. if len(o.HostFingerprints) > 0 {
  43. if err := addHostFingerprints(o.HostFingerprints); err != nil {
  44. logrus.WithError(err).Error("failed to add host fingerprints")
  45. }
  46. }
  47. var numWorkers int
  48. if o.MaxParallelWorkers != 0 {
  49. numWorkers = o.MaxParallelWorkers
  50. } else {
  51. numWorkers = len(o.GitRefs)
  52. }
  53. wg := &sync.WaitGroup{}
  54. wg.Add(numWorkers)
  55. input := make(chan kube.Refs)
  56. output := make(chan clone.Record, len(o.GitRefs))
  57. for i := 0; i < numWorkers; i++ {
  58. go func() {
  59. defer wg.Done()
  60. for ref := range input {
  61. output <- cloneFunc(ref, o.SrcRoot, o.GitUserName, o.GitUserEmail, o.CookiePath, env)
  62. }
  63. }()
  64. }
  65. for _, ref := range o.GitRefs {
  66. input <- ref
  67. }
  68. close(input)
  69. wg.Wait()
  70. close(output)
  71. var results []clone.Record
  72. for record := range output {
  73. results = append(results, record)
  74. }
  75. logData, err := json.Marshal(results)
  76. if err != nil {
  77. return fmt.Errorf("failed to marshal clone records: %v", err)
  78. }
  79. if err := ioutil.WriteFile(o.Log, logData, 0755); err != nil {
  80. return fmt.Errorf("failed to write clone records: %v", err)
  81. }
  82. return nil
  83. }
  84. func addHostFingerprints(fingerprints []string) error {
  85. path := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
  86. f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  87. if err != nil {
  88. return fmt.Errorf("could not create/append to %s: %v", path, err)
  89. }
  90. if _, err := f.Write([]byte(strings.Join(fingerprints, "\n"))); err != nil {
  91. return fmt.Errorf("failed to write fingerprints to %s: %v", path, err)
  92. }
  93. if err := f.Close(); err != nil {
  94. return fmt.Errorf("failed to close %s: %v", path, err)
  95. }
  96. return nil
  97. }
  98. // addSSHKeys will start the ssh-agent and add all the specified
  99. // keys, returning the ssh-agent environment variables for reuse
  100. func addSSHKeys(paths []string) ([]string, error) {
  101. vars, err := exec.Command("ssh-agent").CombinedOutput()
  102. if err != nil {
  103. return []string{}, fmt.Errorf("failed to start ssh-agent: %v", err)
  104. }
  105. logrus.Info("Started SSH agent")
  106. // ssh-agent will output three lines of text, in the form:
  107. // SSH_AUTH_SOCK=xxx; export SSH_AUTH_SOCK;
  108. // SSH_AGENT_PID=xxx; export SSH_AGENT_PID;
  109. // echo Agent pid xxx;
  110. // We need to parse out the environment variables from that.
  111. parts := strings.Split(string(vars), ";")
  112. env := []string{strings.TrimSpace(parts[0]), strings.TrimSpace(parts[2])}
  113. for _, keyPath := range paths {
  114. // we can be given literal paths to keys or paths to dirs
  115. // that are mounted from a secret, so we need to check which
  116. // we have
  117. if err := filepath.Walk(keyPath, func(path string, info os.FileInfo, err error) error {
  118. if strings.HasPrefix(info.Name(), "..") {
  119. // kubernetes volumes also include files we
  120. // should not look be looking into for keys
  121. if info.IsDir() {
  122. return filepath.SkipDir
  123. }
  124. return nil
  125. }
  126. if info.IsDir() {
  127. return nil
  128. }
  129. cmd := exec.Command("ssh-add", path)
  130. cmd.Env = append(cmd.Env, env...)
  131. if output, err := cmd.CombinedOutput(); err != nil {
  132. return fmt.Errorf("failed to add ssh key at %s: %v: %s", path, err, output)
  133. }
  134. logrus.Infof("Added SSH key at %s", path)
  135. return nil
  136. }); err != nil {
  137. return env, fmt.Errorf("error walking path %q: %v", keyPath, err)
  138. }
  139. }
  140. return env, nil
  141. }