You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
4.5 KiB
161 lines
4.5 KiB
/* |
|
Copyright 2018 The Kubernetes Authors. |
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
you may not use this file except in compliance with the License. |
|
You may obtain a copy of the License at |
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
See the License for the specific language governing permissions and |
|
limitations under the License. |
|
*/ |
|
|
|
package clonerefs |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"io/ioutil" |
|
"os" |
|
"os/exec" |
|
"path/filepath" |
|
"strings" |
|
"sync" |
|
|
|
"github.com/sirupsen/logrus" |
|
"k8s.io/test-infra/prow/kube" |
|
"k8s.io/test-infra/prow/pod-utils/clone" |
|
) |
|
|
|
var cloneFunc = clone.Run |
|
|
|
// Run clones the configured refs |
|
func (o Options) Run() error { |
|
var env []string |
|
if len(o.KeyFiles) > 0 { |
|
var err error |
|
env, err = addSSHKeys(o.KeyFiles) |
|
if err != nil { |
|
logrus.WithError(err).Error("Failed to add SSH keys.") |
|
// Continue on error. Clones will fail with an appropriate error message |
|
// that initupload can consume whereas quitting without writing the clone |
|
// record log is silent and results in an errored prow job instead of a |
|
// failed one. |
|
} |
|
} |
|
if len(o.HostFingerprints) > 0 { |
|
if err := addHostFingerprints(o.HostFingerprints); err != nil { |
|
logrus.WithError(err).Error("failed to add host fingerprints") |
|
} |
|
} |
|
|
|
var numWorkers int |
|
if o.MaxParallelWorkers != 0 { |
|
numWorkers = o.MaxParallelWorkers |
|
} else { |
|
numWorkers = len(o.GitRefs) |
|
} |
|
|
|
wg := &sync.WaitGroup{} |
|
wg.Add(numWorkers) |
|
|
|
input := make(chan kube.Refs) |
|
output := make(chan clone.Record, len(o.GitRefs)) |
|
for i := 0; i < numWorkers; i++ { |
|
go func() { |
|
defer wg.Done() |
|
for ref := range input { |
|
output <- cloneFunc(ref, o.SrcRoot, o.GitUserName, o.GitUserEmail, o.CookiePath, env) |
|
} |
|
}() |
|
} |
|
|
|
for _, ref := range o.GitRefs { |
|
input <- ref |
|
} |
|
|
|
close(input) |
|
wg.Wait() |
|
close(output) |
|
|
|
var results []clone.Record |
|
for record := range output { |
|
results = append(results, record) |
|
} |
|
|
|
logData, err := json.Marshal(results) |
|
if err != nil { |
|
return fmt.Errorf("failed to marshal clone records: %v", err) |
|
} |
|
|
|
if err := ioutil.WriteFile(o.Log, logData, 0755); err != nil { |
|
return fmt.Errorf("failed to write clone records: %v", err) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func addHostFingerprints(fingerprints []string) error { |
|
path := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts") |
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) |
|
if err != nil { |
|
return fmt.Errorf("could not create/append to %s: %v", path, err) |
|
} |
|
if _, err := f.Write([]byte(strings.Join(fingerprints, "\n"))); err != nil { |
|
return fmt.Errorf("failed to write fingerprints to %s: %v", path, err) |
|
} |
|
if err := f.Close(); err != nil { |
|
return fmt.Errorf("failed to close %s: %v", path, err) |
|
} |
|
return nil |
|
} |
|
|
|
// addSSHKeys will start the ssh-agent and add all the specified |
|
// keys, returning the ssh-agent environment variables for reuse |
|
func addSSHKeys(paths []string) ([]string, error) { |
|
vars, err := exec.Command("ssh-agent").CombinedOutput() |
|
if err != nil { |
|
return []string{}, fmt.Errorf("failed to start ssh-agent: %v", err) |
|
} |
|
logrus.Info("Started SSH agent") |
|
// ssh-agent will output three lines of text, in the form: |
|
// SSH_AUTH_SOCK=xxx; export SSH_AUTH_SOCK; |
|
// SSH_AGENT_PID=xxx; export SSH_AGENT_PID; |
|
// echo Agent pid xxx; |
|
// We need to parse out the environment variables from that. |
|
parts := strings.Split(string(vars), ";") |
|
env := []string{strings.TrimSpace(parts[0]), strings.TrimSpace(parts[2])} |
|
for _, keyPath := range paths { |
|
// we can be given literal paths to keys or paths to dirs |
|
// that are mounted from a secret, so we need to check which |
|
// we have |
|
if err := filepath.Walk(keyPath, func(path string, info os.FileInfo, err error) error { |
|
if strings.HasPrefix(info.Name(), "..") { |
|
// kubernetes volumes also include files we |
|
// should not look be looking into for keys |
|
if info.IsDir() { |
|
return filepath.SkipDir |
|
} |
|
return nil |
|
} |
|
if info.IsDir() { |
|
return nil |
|
} |
|
|
|
cmd := exec.Command("ssh-add", path) |
|
cmd.Env = append(cmd.Env, env...) |
|
if output, err := cmd.CombinedOutput(); err != nil { |
|
return fmt.Errorf("failed to add ssh key at %s: %v: %s", path, err, output) |
|
} |
|
logrus.Infof("Added SSH key at %s", path) |
|
return nil |
|
}); err != nil { |
|
return env, fmt.Errorf("error walking path %q: %v", keyPath, err) |
|
} |
|
} |
|
return env, nil |
|
}
|
|
|