-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
entrypointer.go
173 lines (148 loc) · 4.67 KB
/
entrypointer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
Copyright 2019 The Tekton 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 entrypoint
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/tektoncd/pipeline/pkg/apis/pipeline"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/termination"
"go.uber.org/zap"
)
//RFC3339 with millisecond
const (
timeFormat = "2006-01-02T15:04:05.000Z07:00"
)
// Entrypointer holds fields for running commands with redirected
// entrypoints.
type Entrypointer struct {
// Entrypoint is the original specified entrypoint, if any.
Entrypoint string
// Args are the original specified args, if any.
Args []string
// WaitFiles is the set of files to wait for. If empty, execution
// begins immediately.
WaitFiles []string
// WaitFileContent indicates the WaitFile should have non-zero size
// before continuing with execution.
WaitFileContent bool
// PostFile is the file to write when complete. If not specified, no
// file is written.
PostFile string
// Termination path is the path of a file to write the starting time of this endpopint
TerminationPath string
// Waiter encapsulates waiting for files to exist.
Waiter Waiter
// Runner encapsulates running commands.
Runner Runner
// PostWriter encapsulates writing files when complete.
PostWriter PostWriter
// Results is the set of files that might contain task results
Results []string
}
// Waiter encapsulates waiting for files to exist.
type Waiter interface {
// Wait blocks until the specified file exists.
Wait(file string, expectContent bool) error
}
// Runner encapsulates running commands.
type Runner interface {
Run(args ...string) error
}
// PostWriter encapsulates writing a file when complete.
type PostWriter interface {
// Write writes to the path when complete.
Write(file string)
}
// Go optionally waits for a file, runs the command, and writes a
// post file.
func (e Entrypointer) Go() error {
prod, _ := zap.NewProduction()
logger := prod.Sugar()
output := []v1beta1.PipelineResourceResult{}
defer func() {
if wErr := termination.WriteMessage(e.TerminationPath, output); wErr != nil {
logger.Fatalf("Error while writing message: %s", wErr)
}
_ = logger.Sync()
}()
for _, f := range e.WaitFiles {
if err := e.Waiter.Wait(f, e.WaitFileContent); err != nil {
// An error happened while waiting, so we bail
// *but* we write postfile to make next steps bail too.
e.WritePostFile(e.PostFile, err)
output = append(output, v1beta1.PipelineResourceResult{
Key: "StartedAt",
Value: time.Now().Format(timeFormat),
})
return err
}
}
if e.Entrypoint != "" {
e.Args = append([]string{e.Entrypoint}, e.Args...)
}
output = append(output, v1beta1.PipelineResourceResult{
Key: "StartedAt",
Value: time.Now().Format(timeFormat),
})
err := e.Runner.Run(e.Args...)
// Write the post file *no matter what*
e.WritePostFile(e.PostFile, err)
// strings.Split(..) with an empty string returns an array that contains one element, an empty string.
// This creates an error when trying to open the result folder as a file.
if len(e.Results) >= 1 && e.Results[0] != "" {
if err := e.readResultsFromDisk(); err != nil {
logger.Fatalf("Error while handling results: %s", err)
}
}
return err
}
func (e Entrypointer) readResultsFromDisk() error {
output := []v1beta1.PipelineResourceResult{}
for _, resultFile := range e.Results {
if resultFile == "" {
continue
}
fileContents, err := ioutil.ReadFile(filepath.Join(pipeline.DefaultResultPath, resultFile))
if os.IsNotExist(err) {
continue
} else if err != nil {
return err
}
// if the file doesn't exist, ignore it
output = append(output, v1beta1.PipelineResourceResult{
Key: resultFile,
Value: string(fileContents),
ResultType: v1beta1.TaskRunResultType,
})
}
// push output to termination path
if len(output) != 0 {
if err := termination.WriteMessage(e.TerminationPath, output); err != nil {
return err
}
}
return nil
}
// WritePostFile write the postfile
func (e Entrypointer) WritePostFile(postFile string, err error) {
if err != nil && postFile != "" {
postFile = fmt.Sprintf("%s.err", postFile)
}
if postFile != "" {
e.PostWriter.Write(postFile)
}
}