@@ -16,10 +16,12 @@ package leader
16
16
17
17
import (
18
18
"context"
19
+ "fmt"
20
+ "io/ioutil"
21
+ "os"
22
+ "strings"
19
23
"time"
20
24
21
- "github.com/operator-framework/operator-sdk/pkg/k8sutil"
22
-
23
25
corev1 "k8s.io/api/core/v1"
24
26
apierrors "k8s.io/apimachinery/pkg/api/errors"
25
27
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,6 +31,29 @@ import (
29
31
logf "sigs.k8s.io/controller-runtime/pkg/log"
30
32
)
31
33
34
+ type runModeType string
35
+
36
+ const (
37
+ localRunMode runModeType = "local"
38
+ clusterRunMode runModeType = "cluster"
39
+ )
40
+
41
+ // forceRunModeEnv indicates if the operator should be forced to run in either local
42
+ // or cluster mode (currently only used for local mode)
43
+ var forceRunModeEnv = "OSDK_FORCE_RUN_MODE"
44
+
45
+ // errNoNamespace indicates that a namespace could not be found for the current
46
+ // environment
47
+ var errNoNamespace = fmt .Errorf ("namespace not found for current environment" )
48
+
49
+ // errRunLocal indicates that the operator is set to run in local mode (this error
50
+ // is returned by functions that only work on operators running in cluster mode)
51
+ var errRunLocal = fmt .Errorf ("operator run mode forced to local" )
52
+
53
+ // podNameEnvVar is the constant for env variable POD_NAME
54
+ // which is the name of the current pod.
55
+ const podNameEnvVar = "POD_NAME"
56
+
32
57
var log = logf .Log .WithName ("leader" )
33
58
34
59
// maxBackoffInterval defines the maximum amount of time to wait between
@@ -45,9 +70,9 @@ const maxBackoffInterval = time.Second * 16
45
70
func Become (ctx context.Context , lockName string ) error {
46
71
log .Info ("Trying to become the leader." )
47
72
48
- ns , err := k8sutil . GetOperatorNamespace ()
73
+ ns , err := getOperatorNamespace ()
49
74
if err != nil {
50
- if err == k8sutil . ErrNoNamespace || err == k8sutil . ErrRunLocal {
75
+ if err == errNoNamespace || err == errRunLocal {
51
76
log .Info ("Skipping leader election; not running in a cluster." )
52
77
return nil
53
78
}
@@ -164,7 +189,7 @@ func Become(ctx context.Context, lockName string) error {
164
189
// this code is currently running.
165
190
// It expects the environment variable POD_NAME to be set by the downwards API
166
191
func myOwnerRef (ctx context.Context , client crclient.Client , ns string ) (* metav1.OwnerReference , error ) {
167
- myPod , err := k8sutil . GetPod (ctx , client , ns )
192
+ myPod , err := getPod (ctx , client , ns )
168
193
if err != nil {
169
194
return nil , err
170
195
}
@@ -183,3 +208,56 @@ func isPodEvicted(pod corev1.Pod) bool {
183
208
podEvicted := pod .Status .Reason == "Evicted"
184
209
return podFailed && podEvicted
185
210
}
211
+
212
+ // getOperatorNamespace returns the namespace the operator should be running in.
213
+ func getOperatorNamespace () (string , error ) {
214
+ if isRunModeLocal () {
215
+ return "" , errRunLocal
216
+ }
217
+ nsBytes , err := ioutil .ReadFile ("/var/run/secrets/kubernetes.io/serviceaccount/namespace" )
218
+ if err != nil {
219
+ if os .IsNotExist (err ) {
220
+ return "" , errNoNamespace
221
+ }
222
+ return "" , err
223
+ }
224
+ ns := strings .TrimSpace (string (nsBytes ))
225
+ log .V (1 ).Info ("Found namespace" , "Namespace" , ns )
226
+ return ns , nil
227
+ }
228
+
229
+ func isRunModeLocal () bool {
230
+ return os .Getenv (forceRunModeEnv ) == string (localRunMode )
231
+ }
232
+
233
+ // getPod returns a Pod object that corresponds to the pod in which the code
234
+ // is currently running.
235
+ // It expects the environment variable POD_NAME to be set by the downwards API.
236
+ func getPod (ctx context.Context , client crclient.Client , ns string ) (* corev1.Pod , error ) {
237
+ if isRunModeLocal () {
238
+ return nil , errRunLocal
239
+ }
240
+ podName := os .Getenv (podNameEnvVar )
241
+ if podName == "" {
242
+ return nil , fmt .Errorf ("required env %s not set, please configure downward API" , podNameEnvVar )
243
+ }
244
+
245
+ log .V (1 ).Info ("Found podname" , "Pod.Name" , podName )
246
+
247
+ pod := & corev1.Pod {}
248
+ key := crclient.ObjectKey {Namespace : ns , Name : podName }
249
+ err := client .Get (ctx , key , pod )
250
+ if err != nil {
251
+ log .Error (err , "Failed to get Pod" , "Pod.Namespace" , ns , "Pod.Name" , podName )
252
+ return nil , err
253
+ }
254
+
255
+ // .Get() clears the APIVersion and Kind,
256
+ // so we need to set them before returning the object.
257
+ pod .TypeMeta .APIVersion = "v1"
258
+ pod .TypeMeta .Kind = "Pod"
259
+
260
+ log .V (1 ).Info ("Found Pod" , "Pod.Namespace" , ns , "Pod.Name" , pod .Name )
261
+
262
+ return pod , nil
263
+ }
0 commit comments