mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-10-21 18:55:02 +00:00
removed legacy code, updated cli
This commit is contained in:
329
agent/agent.go
329
agent/agent.go
@@ -1,329 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/build"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/version"
|
||||
"github.com/drone/drone/yaml"
|
||||
"github.com/drone/drone/yaml/transform"
|
||||
"github.com/drone/envsubst"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Write(*build.Line)
|
||||
}
|
||||
|
||||
type Agent struct {
|
||||
Update UpdateFunc
|
||||
Logger LoggerFunc
|
||||
Engine build.Engine
|
||||
Timeout time.Duration
|
||||
Platform string
|
||||
Namespace string
|
||||
Extension []string
|
||||
Escalate []string
|
||||
Netrc []string
|
||||
Local string
|
||||
Pull bool
|
||||
}
|
||||
|
||||
func (a *Agent) Poll() error {
|
||||
|
||||
// logrus.Infof("Starting build %s/%s#%d.%d",
|
||||
// payload.Repo.Owner, payload.Repo.Name, payload.Build.Number, payload.Job.Number)
|
||||
//
|
||||
//
|
||||
// logrus.Infof("Finished build %s/%s#%d.%d",
|
||||
// payload.Repo.Owner, payload.Repo.Name, payload.Build.Number, payload.Job.Number)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) Run(payload *model.Work, cancel <-chan bool) error {
|
||||
|
||||
payload.Job.Status = model.StatusRunning
|
||||
payload.Job.Started = time.Now().Unix()
|
||||
|
||||
spec, err := a.prep(payload)
|
||||
if err != nil {
|
||||
payload.Job.Error = err.Error()
|
||||
payload.Job.ExitCode = 255
|
||||
payload.Job.Finished = payload.Job.Started
|
||||
payload.Job.Status = model.StatusError
|
||||
a.Update(payload)
|
||||
return err
|
||||
}
|
||||
a.Update(payload)
|
||||
err = a.exec(spec, payload, cancel)
|
||||
|
||||
if err != nil {
|
||||
payload.Job.ExitCode = 255
|
||||
payload.Job.Error = err.Error()
|
||||
}
|
||||
if exitErr, ok := err.(*build.ExitError); ok {
|
||||
payload.Job.ExitCode = exitErr.Code
|
||||
payload.Job.Error = "" // exit errors are already written to the log
|
||||
}
|
||||
|
||||
payload.Job.Finished = time.Now().Unix()
|
||||
|
||||
switch payload.Job.ExitCode {
|
||||
case 128, 130, 137:
|
||||
payload.Job.Status = model.StatusKilled
|
||||
case 0:
|
||||
payload.Job.Status = model.StatusSuccess
|
||||
default:
|
||||
payload.Job.Status = model.StatusFailure
|
||||
}
|
||||
|
||||
a.Update(payload)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Agent) prep(w *model.Work) (*yaml.Config, error) {
|
||||
|
||||
envs := toEnv(w)
|
||||
envSecrets := map[string]string{}
|
||||
|
||||
// list of secrets to interpolate in the yaml
|
||||
for _, secret := range w.Secrets {
|
||||
if (w.Verified || secret.SkipVerify) && secret.MatchEvent(w.Build.Event) {
|
||||
envSecrets[secret.Name] = secret.Value
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
w.Yaml, err = envsubst.Eval(w.Yaml, func(s string) string {
|
||||
env, ok := envSecrets[s]
|
||||
if !ok {
|
||||
env, _ = envs[s]
|
||||
}
|
||||
if strings.Contains(env, "\n") {
|
||||
env = fmt.Sprintf("%q", env)
|
||||
}
|
||||
return env
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// append secrets when verified or when a secret does not require
|
||||
// verification
|
||||
var secrets []*model.Secret
|
||||
for _, secret := range w.Secrets {
|
||||
if w.Verified || secret.SkipVerify {
|
||||
secrets = append(secrets, secret)
|
||||
}
|
||||
}
|
||||
|
||||
// inject the netrc file into the clone plugin if the repository is
|
||||
// private and requires authentication.
|
||||
if w.Repo.IsPrivate {
|
||||
secrets = append(secrets, &model.Secret{
|
||||
Name: "DRONE_NETRC_USERNAME",
|
||||
Value: w.Netrc.Login,
|
||||
Images: []string{"*"},
|
||||
Events: []string{"*"},
|
||||
})
|
||||
secrets = append(secrets, &model.Secret{
|
||||
Name: "DRONE_NETRC_PASSWORD",
|
||||
Value: w.Netrc.Password,
|
||||
Images: []string{"*"},
|
||||
Events: []string{"*"},
|
||||
})
|
||||
secrets = append(secrets, &model.Secret{
|
||||
Name: "DRONE_NETRC_MACHINE",
|
||||
Value: w.Netrc.Machine,
|
||||
Images: []string{"*"},
|
||||
Events: []string{"*"},
|
||||
})
|
||||
}
|
||||
|
||||
conf, err := yaml.ParseString(w.Yaml)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src := "src"
|
||||
if url, _ := url.Parse(w.Repo.Link); url != nil {
|
||||
host, _, err := net.SplitHostPort(url.Host)
|
||||
if err == nil {
|
||||
url.Host = host
|
||||
}
|
||||
src = filepath.Join(src, url.Host, url.Path)
|
||||
}
|
||||
|
||||
transform.Clone(conf, w.Repo.Kind)
|
||||
transform.Environ(conf, envs)
|
||||
transform.DefaultFilter(conf)
|
||||
if w.BuildLast != nil {
|
||||
transform.ChangeFilter(conf, w.BuildLast.Status)
|
||||
}
|
||||
|
||||
transform.ImageSecrets(conf, secrets, w.Build.Event)
|
||||
transform.Identifier(conf)
|
||||
transform.WorkspaceTransform(conf, "/drone", src)
|
||||
|
||||
if err := transform.Check(conf, w.Repo.IsTrusted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transform.CommandTransform(conf)
|
||||
transform.ImagePull(conf, a.Pull)
|
||||
transform.ImageTag(conf)
|
||||
if err := transform.ImageEscalate(conf, a.Escalate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transform.PluginParams(conf)
|
||||
|
||||
if a.Local != "" {
|
||||
transform.PluginDisable(conf, true)
|
||||
transform.ImageVolume(conf, []string{a.Local + ":" + conf.Workspace.Path})
|
||||
}
|
||||
|
||||
transform.Pod(conf, a.Platform)
|
||||
if err := transform.RemoteTransform(conf, a.Extension); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func (a *Agent) exec(spec *yaml.Config, payload *model.Work, cancel <-chan bool) error {
|
||||
|
||||
conf := build.Config{
|
||||
Engine: a.Engine,
|
||||
Buffer: 500,
|
||||
}
|
||||
|
||||
pipeline := conf.Pipeline(spec)
|
||||
defer pipeline.Teardown()
|
||||
|
||||
// setup the build environment
|
||||
if err := pipeline.Setup(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
replacer := NewSecretReplacer(payload.Secrets)
|
||||
timeout := time.After(time.Duration(payload.Repo.Timeout) * time.Minute)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-pipeline.Done():
|
||||
return pipeline.Err()
|
||||
case <-cancel:
|
||||
pipeline.Stop()
|
||||
return fmt.Errorf("termination request received, build cancelled")
|
||||
case <-timeout:
|
||||
pipeline.Stop()
|
||||
return fmt.Errorf("maximum time limit exceeded, build cancelled")
|
||||
case <-time.After(a.Timeout):
|
||||
pipeline.Stop()
|
||||
return fmt.Errorf("terminal inactive for %v, build cancelled", a.Timeout)
|
||||
case <-pipeline.Next():
|
||||
|
||||
// TODO(bradrydzewski) this entire block of code should probably get
|
||||
// encapsulated in the pipeline.
|
||||
status := model.StatusSuccess
|
||||
if pipeline.Err() != nil {
|
||||
status = model.StatusFailure
|
||||
}
|
||||
// updates the build status passed into each container. I realize this is
|
||||
// a bit out of place and will work to resolve.
|
||||
pipeline.Head().Environment["DRONE_BUILD_STATUS"] = status
|
||||
|
||||
if !pipeline.Head().Constraints.Match(
|
||||
a.Platform,
|
||||
payload.Build.Deploy,
|
||||
payload.Build.Event,
|
||||
payload.Build.Branch,
|
||||
status, payload.Job.Environment) { // TODO: fix this whole section
|
||||
|
||||
pipeline.Skip()
|
||||
} else {
|
||||
pipeline.Exec()
|
||||
}
|
||||
case line := <-pipeline.Pipe():
|
||||
line.Out = replacer.Replace(line.Out)
|
||||
a.Logger(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toEnv(w *model.Work) map[string]string {
|
||||
envs := map[string]string{
|
||||
"CI": "drone",
|
||||
"DRONE": "true",
|
||||
"DRONE_ARCH": "linux/amd64",
|
||||
"DRONE_REPO": w.Repo.FullName,
|
||||
"DRONE_REPO_SCM": w.Repo.Kind,
|
||||
"DRONE_REPO_OWNER": w.Repo.Owner,
|
||||
"DRONE_REPO_NAME": w.Repo.Name,
|
||||
"DRONE_REPO_LINK": w.Repo.Link,
|
||||
"DRONE_REPO_AVATAR": w.Repo.Avatar,
|
||||
"DRONE_REPO_BRANCH": w.Repo.Branch,
|
||||
"DRONE_REPO_PRIVATE": fmt.Sprintf("%v", w.Repo.IsPrivate),
|
||||
"DRONE_REPO_TRUSTED": fmt.Sprintf("%v", w.Repo.IsTrusted),
|
||||
"DRONE_REMOTE_URL": w.Repo.Clone,
|
||||
"DRONE_COMMIT_SHA": w.Build.Commit,
|
||||
"DRONE_COMMIT_REF": w.Build.Ref,
|
||||
"DRONE_COMMIT_REFSPEC": w.Build.Refspec,
|
||||
"DRONE_COMMIT_BRANCH": w.Build.Branch,
|
||||
"DRONE_COMMIT_LINK": w.Build.Link,
|
||||
"DRONE_COMMIT_MESSAGE": w.Build.Message,
|
||||
"DRONE_COMMIT_AUTHOR": w.Build.Author,
|
||||
"DRONE_COMMIT_AUTHOR_EMAIL": w.Build.Email,
|
||||
"DRONE_COMMIT_AUTHOR_AVATAR": w.Build.Avatar,
|
||||
"DRONE_BUILD_NUMBER": fmt.Sprintf("%d", w.Build.Number),
|
||||
"DRONE_BUILD_EVENT": w.Build.Event,
|
||||
"DRONE_BUILD_STATUS": w.Build.Status,
|
||||
"DRONE_BUILD_LINK": fmt.Sprintf("%s/%s/%d", w.System.Link, w.Repo.FullName, w.Build.Number),
|
||||
"DRONE_BUILD_CREATED": fmt.Sprintf("%d", w.Build.Created),
|
||||
"DRONE_BUILD_STARTED": fmt.Sprintf("%d", w.Build.Started),
|
||||
"DRONE_BUILD_FINISHED": fmt.Sprintf("%d", w.Build.Finished),
|
||||
"DRONE_JOB_NUMBER": fmt.Sprintf("%d", w.Job.Number),
|
||||
"DRONE_JOB_STATUS": w.Job.Status,
|
||||
"DRONE_JOB_ERROR": w.Job.Error,
|
||||
"DRONE_JOB_EXIT_CODE": fmt.Sprintf("%d", w.Job.ExitCode),
|
||||
"DRONE_JOB_STARTED": fmt.Sprintf("%d", w.Job.Started),
|
||||
"DRONE_JOB_FINISHED": fmt.Sprintf("%d", w.Job.Finished),
|
||||
"DRONE_YAML_VERIFIED": fmt.Sprintf("%v", w.Verified),
|
||||
"DRONE_YAML_SIGNED": fmt.Sprintf("%v", w.Signed),
|
||||
"DRONE_BRANCH": w.Build.Branch,
|
||||
"DRONE_COMMIT": w.Build.Commit,
|
||||
"DRONE_VERSION": version.Version,
|
||||
}
|
||||
|
||||
if w.Build.Event == model.EventTag {
|
||||
envs["DRONE_TAG"] = strings.TrimPrefix(w.Build.Ref, "refs/tags/")
|
||||
}
|
||||
if w.Build.Event == model.EventPull {
|
||||
envs["DRONE_PULL_REQUEST"] = pullRegexp.FindString(w.Build.Ref)
|
||||
}
|
||||
if w.Build.Event == model.EventDeploy {
|
||||
envs["DRONE_DEPLOY_TO"] = w.Build.Deploy
|
||||
}
|
||||
|
||||
if w.BuildLast != nil {
|
||||
envs["DRONE_PREV_BUILD_STATUS"] = w.BuildLast.Status
|
||||
envs["DRONE_PREV_BUILD_NUMBER"] = fmt.Sprintf("%v", w.BuildLast.Number)
|
||||
envs["DRONE_PREV_COMMIT_SHA"] = w.BuildLast.Commit
|
||||
}
|
||||
|
||||
// inject matrix values as environment variables
|
||||
for key, val := range w.Job.Environment {
|
||||
envs[key] = val
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
var pullRegexp = regexp.MustCompile("\\d+")
|
@@ -1,46 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
)
|
||||
|
||||
// SecretReplacer hides secrets from being exposed by the build output.
|
||||
type SecretReplacer interface {
|
||||
// Replace conceals instances of secrets found in s.
|
||||
Replace(s string) string
|
||||
}
|
||||
|
||||
// NewSecretReplacer creates a SecretReplacer based on whether any value in
|
||||
// secrets requests it be hidden.
|
||||
func NewSecretReplacer(secrets []*model.Secret) SecretReplacer {
|
||||
var r []string
|
||||
for _, s := range secrets {
|
||||
if s.Conceal {
|
||||
r = append(r, s.Value, "*****")
|
||||
}
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return &noopReplacer{}
|
||||
}
|
||||
|
||||
return &secretReplacer{
|
||||
replacer: strings.NewReplacer(r...),
|
||||
}
|
||||
}
|
||||
|
||||
type noopReplacer struct{}
|
||||
|
||||
func (*noopReplacer) Replace(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
type secretReplacer struct {
|
||||
replacer *strings.Replacer
|
||||
}
|
||||
|
||||
func (r *secretReplacer) Replace(s string) string {
|
||||
return r.replacer.Replace(s)
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/franela/goblin"
|
||||
)
|
||||
|
||||
const testString = "This is SECRET: secret_value"
|
||||
|
||||
func TestSecret(t *testing.T) {
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("SecretReplacer", func() {
|
||||
g.It("Should conceal secret", func() {
|
||||
secrets := []*model.Secret{
|
||||
{
|
||||
Name: "SECRET",
|
||||
Value: "secret_value",
|
||||
Conceal: true,
|
||||
},
|
||||
}
|
||||
r := NewSecretReplacer(secrets)
|
||||
g.Assert(r.Replace(testString)).Equal("This is SECRET: *****")
|
||||
})
|
||||
|
||||
g.It("Should not conceal secret", func() {
|
||||
secrets := []*model.Secret{
|
||||
{
|
||||
Name: "SECRET",
|
||||
Value: "secret_value",
|
||||
Conceal: false,
|
||||
},
|
||||
}
|
||||
r := NewSecretReplacer(secrets)
|
||||
g.Assert(r.Replace(testString)).Equal(testString)
|
||||
})
|
||||
})
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/build"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/mq/logger"
|
||||
"github.com/drone/mq/stomp"
|
||||
)
|
||||
|
||||
// UpdateFunc handles buid pipeline status updates.
|
||||
type UpdateFunc func(*model.Work)
|
||||
|
||||
// LoggerFunc handles buid pipeline logging updates.
|
||||
type LoggerFunc func(*build.Line)
|
||||
|
||||
var NoopUpdateFunc = func(*model.Work) {}
|
||||
|
||||
var TermLoggerFunc = func(line *build.Line) {
|
||||
fmt.Println(line)
|
||||
}
|
||||
|
||||
// NewClientUpdater returns an updater that sends updated build details
|
||||
// to the drone server.
|
||||
func NewClientUpdater(client *stomp.Client) UpdateFunc {
|
||||
return func(w *model.Work) {
|
||||
err := client.SendJSON("/queue/updates", w)
|
||||
if err != nil {
|
||||
logger.Warningf("Error updating %s/%s#%d.%d. %s",
|
||||
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number, err)
|
||||
}
|
||||
if w.Job.Status != model.StatusRunning {
|
||||
var dest = fmt.Sprintf("/topic/logs.%d", w.Job.ID)
|
||||
var opts = []stomp.MessageOption{
|
||||
stomp.WithHeader("eof", "true"),
|
||||
stomp.WithRetain("all"),
|
||||
}
|
||||
|
||||
if err := client.Send(dest, []byte("eof"), opts...); err != nil {
|
||||
logger.Warningf("Error sending eof %s/%s#%d.%d. %s",
|
||||
w.Repo.Owner, w.Repo.Name, w.Build.Number, w.Job.Number, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientLogger(client *stomp.Client, id int64, limit int64) LoggerFunc {
|
||||
|
||||
var size int64
|
||||
var dest = fmt.Sprintf("/topic/logs.%d", id)
|
||||
var opts = []stomp.MessageOption{
|
||||
stomp.WithRetain("all"),
|
||||
}
|
||||
|
||||
return func(line *build.Line) {
|
||||
if size > limit {
|
||||
return
|
||||
}
|
||||
if err := client.SendJSON(dest, line, opts...); err != nil {
|
||||
logrus.Errorf("Error streaming build logs. %s", err)
|
||||
}
|
||||
|
||||
size += int64(len(line.Out))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user