-
Notifications
You must be signed in to change notification settings - Fork 115
/
runner.go
190 lines (151 loc) · 4.94 KB
/
runner.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Package runner provides a wrapper for the OpenShift extended test suite image.
package runner
import (
"context"
"fmt"
"github.com/go-logr/logr"
"github.com/onsi/ginkgo/v2"
image "github.com/openshift/client-go/image/clientset/versioned"
"github.com/openshift/osde2e/pkg/common/util"
kubev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kube "k8s.io/client-go/kubernetes"
)
const (
// name used in Runner resources
defaultName = "osde2e-runner"
// directory containing service account credentials
serviceAccountDir = "/var/run/secrets/kubernetes.io/serviceaccount"
)
// DefaultRunner is a runner with the most commonly desired settings.
var DefaultRunner = &Runner{
Name: defaultName,
ImageStreamName: testImageStreamName,
ImageStreamNamespace: testImageStreamNamespace,
PodSpec: kubev1.PodSpec{
Containers: []kubev1.Container{
DefaultContainer,
},
RestartPolicy: kubev1.RestartPolicyNever,
},
OutputDir: "/test-run-results",
Server: "https://kubernetes.default",
CA: serviceAccountDir + "/ca.crt",
TokenFile: serviceAccountDir + "/token",
Logger: ginkgo.GinkgoLogr,
}
// Runner runs the OpenShift extended test suite within a cluster.
type Runner struct {
// Kube client used to run test in-cluster.
Kube kube.Interface
// Image client used to gather ImageStream information.
Image image.Interface
// Name of the operation being performed.
Name string
// Server is the endpoint within the pod the Kubernetes API should be
Server string
// CA is the CA bundle used to auth against the Kubernetes API
CA string
// TokenFile is the credentials used to auth against the Kubernetes API
TokenFile string
// Namespace runner resources should be created in.
Namespace string
// ImageStreamName is the name of the ImageStream containing the suite.
ImageStreamName string
// ImageStreamNamespace is the namespace of the ImageStream containing the suite.
ImageStreamNamespace string
// ImageName is a container image used for the runner.
ImageName string
// Cmd is run within the test pod. If PodSpec is also set it overrides the container of the same name.
Cmd string
// PodSpec defines the Pod used by the runner.
PodSpec kubev1.PodSpec
// OutputDir is the directory that is copied from the Pod to the local host.
OutputDir string
// Tarball will create a single .tgz file for the entire OutputDir.
Tarball bool
// SkipLogsFromPod should be set to true if logs should not be collected.
SkipLogsFromPod bool
// Repos are cloned and mounted into the test Pod.
Repos
// Logger receives all messages.
logr.Logger
// internal
stopCh <-chan struct{}
svc *kubev1.Service
status Status
}
// Run deploys the suite into a cluster, waits for it to finish, and gathers the results.
func (r *Runner) Run(timeoutInSeconds int, stopCh <-chan struct{}) (err error) {
// TODO: pass this in
ctx := context.TODO()
r.stopCh = stopCh
r.status = StatusSetup
// set image if imagestream is set
if r.ImageName == "" {
if r.ImageName, err = r.GetLatestImageStreamTag(); err != nil {
return
}
}
r.Info(fmt.Sprintf("Using '%s' as image for runner", r.ImageName))
r.Info(fmt.Sprintf("Creating %s runner Pod...", r.Name))
var pod *kubev1.Pod
if pod, err = r.createJobPod(ctx); err != nil {
return
}
r.Info(fmt.Sprintf("Waiting for %s runner Pod to start...", r.Name))
if err = r.waitForPodRunning(ctx, pod); err != nil {
return
}
r.status = StatusRunning
r.Info(fmt.Sprintf("Creating service for %s runner Pod...", r.Name))
if r.svc, err = r.createService(ctx, pod); err != nil {
return
}
r.Info(fmt.Sprintf("Waiting for endpoints of %s runner Pod with a timeout of %d seconds...", r.Name, timeoutInSeconds))
var completionErr error
completionErr = r.waitForCompletion(ctx, pod.Name, timeoutInSeconds)
if !r.SkipLogsFromPod {
r.Info(fmt.Sprintf("Collecting logs from containers on %s runner Pod...", r.Name))
if err = r.getAllLogsFromPod(ctx, pod.Name); err != nil {
return
}
} else {
r.Info(fmt.Sprintf("Skipping logs from pod %s", r.Name))
}
if completionErr != nil {
return completionErr
}
r.Info(fmt.Sprintf("%s runner is done", r.Name))
r.status = StatusDone
return nil
}
// Status returns the current state of the runner.
func (r *Runner) Status() Status {
return r.status
}
// DeepCopy returns a deep copy of a runner.
func (r *Runner) DeepCopy() *Runner {
newRunner := *DefaultRunner
// copy repos & PodSpec
newRunner.Repos = make(Repos, len(r.Repos))
copy(newRunner.Repos, r.Repos)
newRunner.PodSpec = *r.PodSpec.DeepCopy()
return &newRunner
}
// meta returns the ObjectMeta used for Runner resources.
func (r *Runner) meta() metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", r.Name, util.RandomStr(5)),
Labels: map[string]string{
"app": r.Name,
},
}
}
// Status is the current state of the runner.
type Status string
var (
StatusSetup Status = "setup"
StatusRunning Status = "running"
StatusDone Status = "done"
)