@@ -31,9 +31,18 @@ import (
31
31
*/
32
32
33
33
type ci struct {
34
- jobSpecFile string
35
- bc * buildConfig
36
- ciBuild string
34
+ apibuild * buildapiv1.Build
35
+ bc * buildConfig
36
+ js * spec.JobSpec
37
+
38
+ pod * v1.Pod
39
+
40
+ hostname string
41
+ image string
42
+ ipaddr string
43
+ jobSpecFile string
44
+ projectNamespace string
45
+ serviceAccount string
37
46
}
38
47
39
48
// CIBuilder is the manual/unbounded Build interface.
@@ -57,16 +66,14 @@ const (
57
66
58
67
func (c * ci ) Exec (ctx context.Context ) error {
59
68
log .Info ("Executing unbounded builder" )
69
+ if c .bc == nil {
70
+ return errors .New ("buildconfig is nil" )
71
+ }
60
72
return c .bc .Exec (ctx )
61
73
}
62
74
63
75
// NewCIBuilder returns a CIBuilder ready for execution.
64
- func NewCIBuilder (ctx context.Context , image , serviceAccount , jsF string ) (CIBuilder , error ) {
65
- // Require a Kubernetes Service Account Client
66
- if err := k8sAPIClient (); err != nil {
67
- return nil , fmt .Errorf ("failed create a kubernetes client: %w" , err )
68
- }
69
-
76
+ func NewCIBuilder (ctx context.Context , inCluster bool , image , serviceAccount , jsF string ) (CIBuilder , error ) {
70
77
// Directly inject the jobspec
71
78
js , err := spec .JobSpecFromFile (jsF )
72
79
if err != nil {
@@ -75,56 +82,71 @@ func NewCIBuilder(ctx context.Context, image, serviceAccount, jsF string) (CIBui
75
82
if js .Recipe .GitURL == "" {
76
83
return nil , errors .New ("JobSpec does inclue a Git Recipe" )
77
84
}
78
- os .Setenv ("SOURCE_REF" , js .Recipe .GitRef )
79
- os .Setenv ("SOURCE_URI" , js .Recipe .GitURL )
80
85
81
- log .WithFields (log.Fields {
82
- "override image" : image ,
83
- "source ref" : js .Recipe .GitRef ,
84
- "source uri" : js .Recipe .GitURL ,
85
- }).Info ("jobspec defined source" )
86
+ c := & ci {
87
+ js : & js ,
88
+ jobSpecFile : jsF ,
89
+ serviceAccount : serviceAccount ,
90
+ image : image ,
91
+ }
86
92
87
- // Get the ci API
88
- ciBuild , err := ciAPIBuild (& js , image , serviceAccount )
93
+ // TODO: implement out-of-cluster
94
+ if err := c .setInCluster (); err != nil {
95
+ return nil , fmt .Errorf ("failed setting incluster options: %v" , err )
96
+ }
97
+
98
+ // Generate the build.openshift.io/v1 object
99
+ if err := c .generateAPIBuild (); err != nil {
100
+ return nil , fmt .Errorf ("failed to generate api build: %v" , err )
101
+ }
102
+ ciBuild , err := c .encodeAPIBuild ()
89
103
if err != nil {
90
- return nil , fmt .Errorf ("failed to create a ci API build object" )
104
+ return nil , fmt .Errorf ("failed to encode apibuild: %v" , err )
91
105
}
92
- os .Setenv ("BUILD" , ciBuild )
93
106
94
107
// Create the buildConfig object
108
+ os .Setenv ("BUILD" , ciBuild )
109
+ os .Setenv ("SOURCE_REF" , js .Recipe .GitRef )
110
+ os .Setenv ("SOURCE_URI" , js .Recipe .GitURL )
95
111
bc , err := newBC ()
96
112
if err != nil {
97
113
return nil , err
98
114
}
99
115
bc .JobSpec = js
100
116
bc .JobSpecFile = jsF
117
+ c .bc = bc
101
118
102
- c := & ci {
103
- jobSpecFile : jsF ,
104
- bc : bc ,
105
- ciBuild : ciBuild ,
106
- }
107
119
return c , nil
108
120
}
109
121
110
- func ciAPIBuild ( js * spec. JobSpec , image , serviceAccount string ) ( string , error ) {
122
+ func ( c * ci ) setInCluster () error {
111
123
// Dig deep and query find out what Kubernetes thinks this pod
112
124
// Discover where this running
113
125
hostname , ok := os .LookupEnv ("HOSTNAME" )
114
126
if ! ok {
115
- return "" , errors .New ("Unable to find hostname" )
127
+ return errors .New ("Unable to find hostname" )
128
+ }
129
+ c .hostname = hostname
130
+
131
+ // Open the Kubernetes Client
132
+ ac , pn , err := k8sInClusterClient ()
133
+ if err != nil {
134
+ return fmt .Errorf ("failed create a kubernetes client: %w" , err )
116
135
}
136
+ c .projectNamespace = pn
117
137
118
- myIP , err := getPodIP (hostname )
138
+ myIP , err := getPodIP (ac , pn , hostname )
119
139
if err != nil {
120
- return "" , fmt .Errorf ("failed to query my hostname: %w" , err )
140
+ return fmt .Errorf ("failed to query my hostname: %w" , err )
121
141
}
142
+ c .ipaddr = myIP
122
143
123
144
// Discover where this running
124
- myPod , err := apiClient . Pods (projectNamespace ).Get (hostname , metav1.GetOptions {})
145
+ myPod , err := ac . CoreV1 (). Pods (pn ).Get (hostname , metav1.GetOptions {})
125
146
if err != nil {
126
- return "" , err
147
+ return err
127
148
}
149
+ c .pod = myPod
128
150
129
151
// find the running pod this is running on.
130
152
var myContainer * v1.Container = nil
@@ -139,24 +161,19 @@ func ciAPIBuild(js *spec.JobSpec, image, serviceAccount string) (string, error)
139
161
}
140
162
141
163
// allow both the service account and the image to be overriden
142
- if serviceAccount == "" {
143
- serviceAccount = myPod .Spec .ServiceAccountName
164
+ if c . serviceAccount == "" {
165
+ c . serviceAccount = myPod .Spec .ServiceAccountName
144
166
}
145
- if image == "" {
146
- image = myContainer .Image
167
+ if c . image == "" {
168
+ c . image = myContainer .Image
147
169
}
148
- if serviceAccount == "" || image == "" {
149
- return "" , errors .New ("serviceAccount and image must be defined by running pod or via overrides" )
170
+ if c . serviceAccount == "" || c . image == "" {
171
+ return errors .New ("serviceAccount and image must be defined by running pod or via overrides" )
150
172
}
173
+ return nil
174
+ }
151
175
152
- l := log .WithFields (log.Fields {
153
- "ip" : myIP ,
154
- "image" : image ,
155
- "serviceAccount" : serviceAccount ,
156
- "host" : myContainer .Name ,
157
- })
158
- l .Info ("identified pod" )
159
-
176
+ func (c * ci ) generateAPIBuild () error {
160
177
// Create just _enough_ of the OpenShift BuildConfig spec
161
178
// Create a "ci" build.openshift.io/v1 specification.
162
179
ciBuildNumber := time .Now ().Format ("20060102150405" )
@@ -167,7 +184,7 @@ func ciAPIBuild(js *spec.JobSpec, image, serviceAccount string) (string, error)
167
184
// ciRunnerTag is tested for to determine if this is
168
185
// a buildconfig or a faked one
169
186
ciRunnerTag : "true" ,
170
- fmt .Sprintf (ciAnnotation , "IP" ): myIP ,
187
+ fmt .Sprintf (ciAnnotation , "IP" ): c . ipaddr ,
171
188
// Required Labels
172
189
buildapiv1 .BuildConfigAnnotation : "ci-cosa-bc" ,
173
190
buildapiv1 .BuildNumberAnnotation : ciBuildNumber ,
@@ -180,25 +197,33 @@ func ciAPIBuild(js *spec.JobSpec, image, serviceAccount string) (string, error)
180
197
181
198
// Populate the Spec
182
199
a .Spec = buildapiv1.BuildSpec {}
183
- a .Spec .ServiceAccount = myPod . Spec . ServiceAccountName
200
+ a .Spec .ServiceAccount = c . serviceAccount
184
201
a .Spec .Strategy = buildapiv1.BuildStrategy {}
185
202
a .Spec .Strategy .CustomStrategy = new (buildapiv1.CustomBuildStrategy )
186
203
a .Spec .Strategy .CustomStrategy .From = corev1.ObjectReference {
187
- Name : image ,
204
+ Name : c . image ,
188
205
}
189
206
a .Spec .Source = buildapiv1.BuildSource {
190
207
ContextDir : cosaSrvDir ,
191
208
Git : & buildapiv1.GitBuildSource {
192
- Ref : js .Recipe .GitRef ,
193
- URI : js .Recipe .GitURL ,
209
+ Ref : c . js .Recipe .GitRef ,
210
+ URI : c . js .Recipe .GitURL ,
194
211
},
195
212
}
196
213
197
- // Render the ci buildapiv1 object to a JSON object.
198
- // JSON is the messaginging interface for Kubernetes.
214
+ c .apibuild = & a
215
+ return nil
216
+ }
217
+
218
+ // encodeAPIBuilder the ci buildapiv1 object to a JSON object.
219
+ // JSON is the messaginging interface for Kubernetes.
220
+ func (c * ci ) encodeAPIBuild () (string , error ) {
221
+ if c .apibuild == nil {
222
+ return "" , errors .New ("apibuild is not defined yet" )
223
+ }
199
224
aW := bytes .NewBuffer ([]byte {})
200
225
s := json .NewYAMLSerializer (json .DefaultMetaFactory , buildScheme , buildScheme )
201
- if err := s .Encode (& a , aW ); err != nil {
226
+ if err := s .Encode (c . apibuild , aW ); err != nil {
202
227
return "" , err
203
228
}
204
229
d , err := ioutil .ReadAll (aW )
0 commit comments