21
21
22
22
import argparse
23
23
import os
24
+ import shutil
24
25
import signal
25
26
import subprocess
26
27
import sys
@@ -44,49 +45,169 @@ def check(*cmd):
44
45
subprocess .check_call (cmd )
45
46
46
47
47
- def sig_handler (_signo , _frame ):
48
- """Stops container upon receive signal.SIGTERM and signal.SIGINT."""
49
- print >> sys .stderr , 'signo = %s, frame = %s' % (_signo , _frame )
50
- check ('docker' , 'stop' , CONTAINER )
48
+ def check_env (env , * cmd ):
49
+ """Log and run the command with a specific env, raising on errors."""
50
+ print >> sys .stderr , 'Environment:'
51
+ for key , value in env .items ():
52
+ print >> sys .stderr , '%s=%s' % (key , value )
53
+ print >> sys .stderr , 'Run:' , cmd
54
+ subprocess .check_call (cmd , env = env )
51
55
52
56
53
57
def kubekins (tag ):
54
58
"""Return full path to kubekins-e2e:tag."""
55
59
return 'gcr.io/k8s-testimages/kubekins-e2e:%s' % tag
56
60
57
61
58
- def main (args ):
59
- """Set up env, start kubekins-e2e, handle termination. """
60
- # pylint: disable=too-many-locals
61
-
62
- # dockerized-e2e-runner goodies setup
63
- workspace = os .environ .get ('WORKSPACE' , os .getcwd ())
64
- artifacts = '%s/_artifacts' % workspace
65
- if not os .path .isdir (artifacts ):
66
- os .makedirs (artifacts )
62
+ class LocalMode (object ):
63
+ """Runs e2e tests by calling e2e-runner.sh."""
64
+ def __init__ (self , workspace ):
65
+ self .workspace = workspace
66
+ self .env = []
67
+ self .env_files = []
68
+ self .add_environment (
69
+ 'HOME=%s' % workspace ,
70
+ 'WORKSPACE=%s' % workspace ,
71
+ 'PATH=%s' % os .getenv ('PATH' ),
72
+ )
73
+
74
+ @staticmethod
75
+ def parse_env (env ):
76
+ """Returns (FOO, BAR=MORE) for FOO=BAR=MORE."""
77
+ return env .split ('=' , 1 )
78
+
79
+ def add_environment (self , * envs ):
80
+ """Adds FOO=BAR to the list of environment overrides."""
81
+ self .env .extend (self .parse_env (e ) for e in envs )
82
+
83
+ def add_files (self , env_files ):
84
+ """Reads all FOO=BAR lines from each path in env_files seq."""
85
+ for env_file in env_files :
86
+ with open (test_infra (env_file )) as fp :
87
+ for line in fp :
88
+ line = line .rstrip ()
89
+ if not line or line .startswith ('#' ):
90
+ continue
91
+ self .env_files .append (self .parse_env (line ))
92
+
93
+ def add_gce_ssh (self , priv , pub ):
94
+ """Copies priv, pub keys to $WORKSPACE/.ssh."""
95
+ gce_ssh = '%s/.ssh/google_compute_engine' % self .workspace
96
+ gce_pub = '%s/.ssh/google_compute_engine.pub' % self .workspace
97
+ shutil .copy (priv , gce_ssh )
98
+ shutil .copy (pub , gce_pub )
99
+ self .add_environment (
100
+ 'JENKINS_GCE_SSH_PRIVATE_KEY_FILE=%s' % gce_ssh ,
101
+ 'JENKINS_GCE_SSH_PUBLIC_KEY_FILE=%s' % gce_pub ,
102
+ )
103
+
104
+ def add_service_account (self , path ):
105
+ """Sets GOOGLE_APPLICATION_CREDENTIALS to path."""
106
+ self .add_environment ('GOOGLE_APPLICATION_CREDENTIALS=%s' % path )
107
+
108
+ @property
109
+ def runner (self ):
110
+ """Finds the best version of e2e-runner.sh."""
111
+ options = ['e2e-runner.sh' , test_infra ('jenkins/e2e-image/e2e-runner.sh' )]
112
+ for path in options :
113
+ if os .path .isfile (path ):
114
+ return path
115
+ raise ValueError ('Cannot find e2e-runner at any of %s' % ', ' .join (options ))
116
+
117
+
118
+ def install_prerequisites (self ):
119
+ """Copies upload-to-gcs and kubetest if needed."""
120
+ parent = os .path .dirname (self .runner )
121
+ if not os .path .isfile (os .path .join (parent , 'upload-to-gcs.sh' )):
122
+ shutil .copy (
123
+ test_infra ('../kubernetes/hack/jenkins/upload-to-gcs.sh' ),
124
+ os .path .join (parent , 'upload-to-gcs.sh' ))
125
+ if not os .path .isfile (os .path .join (parent , 'kubetest' )):
126
+ check ('go' , 'install' , 'k8s.io/test-infra/kubetest' )
127
+ shutil .copy (
128
+ os .path .expandvars ('${GOPATH}/bin/kubetest' ),
129
+ os .path .join (parent , 'kubetest' ))
130
+
131
+ def start (self ):
132
+ """Runs e2e-runner.sh after setting env and installing prereqs."""
133
+ env = {}
134
+ env .update (self .env_files )
135
+ env .update (self .env )
136
+ self .install_prerequisites ()
137
+ check_env (env , self .runner )
138
+
139
+
140
+ class DockerMode (object ):
141
+ """Runs e2e tests via docker run kubekins-e2e."""
142
+ def __init__ (self , container , workspace , sudo , tag , mount_paths ):
143
+ self .tag = tag
144
+ try : # Pull a newer version if one exists
145
+ check ('docker' , 'pull' , kubekins (tag ))
146
+ except subprocess .CalledProcessError :
147
+ pass
148
+
149
+ print 'Starting %s...' % container
150
+
151
+ self .container = container
152
+ self .cmd = [
153
+ 'docker' , 'run' , '--rm' ,
154
+ '--name=%s' % container ,
155
+ '-v' , '%s/_artifacts:/workspace/_artifacts' % workspace ,
156
+ '-v' , '/etc/localtime:/etc/localtime:ro' ,
157
+ ]
158
+ for path in mount_paths :
159
+ self .cmd .extend (['-v' , path ])
160
+
161
+ if sudo :
162
+ self .cmd .extend (['-v' , '/var/run/docker.sock:/var/run/docker.sock' ])
163
+ self .add_environment (
164
+ 'HOME=/workspace' ,
165
+ 'WORKSPACE=/workspace' )
166
+
167
+ def add_environment (self , * envs ):
168
+ """Adds FOO=BAR to the -e list for docker."""
169
+ for env in envs :
170
+ self .cmd .extend (['-e' , env ])
171
+
172
+ def add_files (self , env_files ):
173
+ """Adds each file to the --env-file list."""
174
+ for env_file in env_files :
175
+ self .cmd .extend (['--env-file' , test_infra (env_file )])
176
+
177
+
178
+ def add_gce_ssh (self , priv , pub ):
179
+ """Mounts priv and pub inside the container."""
180
+ gce_ssh = '/workspace/.ssh/google_compute_engine'
181
+ gce_pub = '%s.pub' % gce_ssh
182
+ self .cmd .extend ([
183
+ '-v' , '%s:%s:ro' % (priv , gce_ssh ),
184
+ '-v' , '%s:%s:ro' % (pub , gce_pub ),
185
+ '-e' , 'JENKINS_GCE_SSH_PRIVATE_KEY_FILE=%s' % gce_ssh ,
186
+ '-e' , 'JENKINS_GCE_SSH_PUBLIC_KEY_FILE=%s' % gce_pub ])
67
187
68
- try : # Pull a newer version if one exists
69
- check ('docker' , 'pull' , kubekins (args .tag ))
70
- except subprocess .CalledProcessError :
71
- pass
188
+ def add_service_account (self , path ):
189
+ """Mounts GOOGLE_APPLICATION_CREDENTIALS inside the container."""
190
+ service = '/service-account.json'
191
+ self .cmd .extend ([
192
+ '-v' , '%s:%s:ro' % (path , service ),
193
+ '-e' , 'GOOGLE_APPLICATION_CREDENTIALS=%s' % service ])
72
194
73
- print 'Starting %s...' % CONTAINER
74
195
75
- cmd = [
76
- 'docker' , 'run' , '--rm' ,
77
- '--name=%s' % CONTAINER ,
78
- '-v' , '%s/_artifacts:/workspace/_artifacts' % workspace ,
79
- '-v' , '/etc/localtime:/etc/localtime:ro'
80
- ]
196
+ def start ( self ):
197
+ """Runs kubekins."""
198
+ self . cmd . append ( kubekins ( self . tag ))
199
+ signal . signal ( signal . SIGTERM , self . sig_handler )
200
+ signal . signal ( signal . SIGINT , self . sig_handler )
201
+ check ( * self . cmd )
81
202
82
- if args .docker_in_docker :
83
- cmd .extend ([
84
- '-v' , '/var/run/docker.sock:/var/run/docker.sock' ])
203
+ def sig_handler (self , _signo , _frame ):
204
+ """Stops container upon receive signal.SIGTERM and signal.SIGINT."""
205
+ print >> sys .stderr , 'docker stop (signo=%s, frame=%s)' % (_signo , _frame )
206
+ check ('docker' , 'stop' , self .container )
85
207
86
- if args .mount_paths :
87
- for path in args .mount_paths :
88
- cmd .extend (['-v' , path ])
89
208
209
+ def main (args ):
210
+ """Set up env, start kubekins-e2e, handle termination. """
90
211
# Rules for env var priority here in docker:
91
212
# -e FOO=a -e FOO=b -> FOO=b
92
213
# --env-file FOO=a --env-file FOO=b -> FOO=b
@@ -96,48 +217,49 @@ def main(args):
96
217
# So if you overwrite FOO=c for a local run it will take precedence.
97
218
#
98
219
220
+ # dockerized-e2e-runner goodies setup
221
+ workspace = os .environ .get ('WORKSPACE' , os .getcwd ())
222
+ artifacts = '%s/_artifacts' % workspace
223
+ if not os .path .isdir (artifacts ):
224
+ os .makedirs (artifacts )
225
+
226
+ container = '%s-%s' % (os .environ .get ('JOB_NAME' ), os .environ .get ('BUILD_NUMBER' ))
227
+ if args .mode == 'docker' :
228
+ mode = DockerMode (container , workspace , args .docker_in_docker , args .tag , args .mount_paths )
229
+ elif args .mode == 'local' :
230
+ mode = LocalMode (workspace ) # pylint: disable=redefined-variable-type
231
+ else :
232
+ raise ValueError (args .mode )
99
233
if args .env_file :
100
- for env in args .env_file :
101
- cmd .extend (['--env-file' , test_infra (env )])
234
+ mode .add_files (args .env_file )
102
235
103
236
if args .gce_ssh :
104
- gce_ssh = '/workspace/.ssh/google_compute_engine'
105
- gce_pub = '%s.pub' % gce_ssh
106
- cmd .extend ([
107
- '-v' , '%s:%s:ro' % (args .gce_ssh , gce_ssh ),
108
- '-v' , '%s:%s:ro' % (args .gce_pub , gce_pub ),
109
- '-e' , 'JENKINS_GCE_SSH_PRIVATE_KEY_FILE=%s' % gce_ssh ,
110
- '-e' , 'JENKINS_GCE_SSH_PUBLIC_KEY_FILE=%s' % gce_pub ])
237
+ mode .add_gce_ssh (args .gce_ssh , args .gce_pub )
111
238
112
239
if args .service_account :
113
- service = '/service-account.json'
114
- cmd .extend ([
115
- '-v' , '%s:%s:ro' % (args .service_account , service ),
116
- '-e' , 'GOOGLE_APPLICATION_CREDENTIALS=%s' % service ])
240
+ mode .add_service_account (args .service_account )
117
241
118
- cmd . extend ([
242
+ mode . add_environment (
119
243
# Boilerplate envs
120
244
# Skip gcloud update checking
121
- '-e' , ' CLOUDSDK_COMPONENT_MANAGER_DISABLE_UPDATE_CHECK=true' ,
245
+ 'CLOUDSDK_COMPONENT_MANAGER_DISABLE_UPDATE_CHECK=true' ,
122
246
# Use default component update behavior
123
- '-e' , ' CLOUDSDK_EXPERIMENTAL_FAST_COMPONENT_UPDATE=false' ,
247
+ 'CLOUDSDK_EXPERIMENTAL_FAST_COMPONENT_UPDATE=false' ,
124
248
# E2E
125
- '-e' , ' E2E_UP=%s' % args .up ,
126
- '-e' , ' E2E_TEST=%s' % args .test ,
127
- '-e' , ' E2E_DOWN=%s' % args .down ,
128
- '-e' , ' E2E_NAME=%s' % args .cluster ,
249
+ 'E2E_UP=%s' % args .up ,
250
+ 'E2E_TEST=%s' % args .test ,
251
+ 'E2E_DOWN=%s' % args .down ,
252
+ 'E2E_NAME=%s' % args .cluster ,
129
253
# AWS
130
- '-e' , ' KUBE_AWS_INSTANCE_PREFIX=%s' % args .cluster ,
254
+ 'KUBE_AWS_INSTANCE_PREFIX=%s' % args .cluster ,
131
255
# GCE
132
- '-e' , ' INSTANCE_PREFIX=%s' % args .cluster ,
133
- '-e' , ' KUBE_GCE_NETWORK=%s' % args .cluster ,
134
- '-e' , ' KUBE_GCE_INSTANCE_PREFIX=%s' % args .cluster ,
256
+ 'INSTANCE_PREFIX=%s' % args .cluster ,
257
+ 'KUBE_GCE_NETWORK=%s' % args .cluster ,
258
+ 'KUBE_GCE_INSTANCE_PREFIX=%s' % args .cluster ,
135
259
# GKE
136
- '-e' , 'CLUSTER_NAME=%s' % args .cluster ,
137
- '-e' , 'KUBE_GKE_NETWORK=%s' % args .cluster ,
138
- # Workspace
139
- '-e' , 'HOME=/workspace' ,
140
- '-e' , 'WORKSPACE=/workspace' ])
260
+ 'CLUSTER_NAME=%s' % args .cluster ,
261
+ 'KUBE_GKE_NETWORK=%s' % args .cluster ,
262
+ )
141
263
142
264
# env blacklist.
143
265
# TODO(krzyzacy) change this to a whitelist
@@ -150,25 +272,25 @@ def main(args):
150
272
'WORKSPACE'
151
273
]
152
274
153
- for key , value in os .environ .items ():
154
- if key not in docker_env_ignore :
155
- cmd .extend (['-e' , '%s=%s' % (key , value )])
275
+ # TODO(fejta): delete this
276
+ mode .add_environment (* (
277
+ '%s=%s' % (k , v ) for (k , v ) in os .environ .items ()
278
+ if k not in docker_env_ignore ))
156
279
157
280
# Overwrite JOB_NAME for soak-*-test jobs
158
281
if args .soak_test and os .environ .get ('JOB_NAME' ):
159
- cmd . extend ([ '-e' , ' JOB_NAME=%s' % os .environ .get ('JOB_NAME' ).replace ('-test' , '-deploy' )] )
282
+ mode . add_environment ( ' JOB_NAME=%s' % os .environ .get ('JOB_NAME' ).replace ('-test' , '-deploy' ))
160
283
161
- cmd . append ( kubekins ( args . tag ) )
284
+ mode . start ( )
162
285
163
- signal .signal (signal .SIGTERM , sig_handler )
164
- signal .signal (signal .SIGINT , sig_handler )
165
-
166
- check (* cmd )
167
286
168
287
169
288
if __name__ == '__main__' :
170
289
171
290
PARSER = argparse .ArgumentParser ()
291
+
292
+ PARSER .add_argument (
293
+ '--mode' , default = 'docker' , choices = ['local' , 'docker' ])
172
294
PARSER .add_argument (
173
295
'--env-file' , action = "append" , help = 'Job specific environment file' )
174
296
@@ -207,6 +329,4 @@ def main(args):
207
329
'--up' , default = 'true' , help = 'If we need to set --up in e2e.go' )
208
330
ARGS = PARSER .parse_args ()
209
331
210
- CONTAINER = '%s-%s' % (os .environ .get ('JOB_NAME' ), os .environ .get ('BUILD_NUMBER' ))
211
-
212
332
main (ARGS )
0 commit comments