@@ -23,72 +23,78 @@ import (
23
23
v1 "k8s.io/api/core/v1"
24
24
apierrors "k8s.io/apimachinery/pkg/api/errors"
25
25
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26
27
"k8s.io/apimachinery/pkg/types"
27
28
"k8s.io/apimachinery/pkg/util/intstr"
28
- "k8s.io/client-go/rest"
29
29
crclient "sigs.k8s.io/controller-runtime/pkg/client"
30
+ "sigs.k8s.io/controller-runtime/pkg/client/config"
30
31
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
31
32
)
32
33
33
34
var log = logf .Log .WithName ("metrics" )
34
35
35
- // PrometheusPortName defines the port name used in kubernetes deployment and service resources
36
- const PrometheusPortName = "metrics"
36
+ var trueVar = true
37
+
38
+ const (
39
+ // PrometheusPortName defines the port name used in the metrics Service.
40
+ PrometheusPortName = "metrics"
41
+ )
37
42
38
43
// ExposeMetricsPort creates a Kubernetes Service to expose the passed metrics port.
39
44
func ExposeMetricsPort (ctx context.Context , port int32 ) (* v1.Service , error ) {
45
+ client , err := createClient ()
46
+ if err != nil {
47
+ return nil , fmt .Errorf ("failed to create new client: %v" , err )
48
+ }
40
49
// We do not need to check the validity of the port, as controller-runtime
41
50
// would error out and we would never get to this stage.
42
- s , err := initOperatorService (port , PrometheusPortName )
51
+ s , err := initOperatorService (ctx , client , port , PrometheusPortName )
43
52
if err != nil {
44
53
if err == k8sutil .ErrNoNamespace {
45
54
log .Info ("Skipping metrics Service creation; not running in a cluster." )
46
55
return nil , nil
47
56
}
48
57
return nil , fmt .Errorf ("failed to initialize service object for metrics: %v" , err )
49
58
}
50
- service , err := createService (ctx , s )
59
+ service , err := createOrUpdateService (ctx , client , s )
51
60
if err != nil {
52
61
return nil , fmt .Errorf ("failed to create or get service for metrics: %v" , err )
53
62
}
54
63
55
64
return service , nil
56
65
}
57
66
58
- func createService (ctx context.Context , s * v1.Service ) (* v1.Service , error ) {
59
- config , err := rest .InClusterConfig ()
60
- if err != nil {
61
- return nil , err
62
- }
63
-
64
- client , err := crclient .New (config , crclient.Options {})
65
- if err != nil {
66
- return nil , err
67
- }
68
-
67
+ func createOrUpdateService (ctx context.Context , client crclient.Client , s * v1.Service ) (* v1.Service , error ) {
69
68
if err := client .Create (ctx , s ); err != nil {
70
69
if ! apierrors .IsAlreadyExists (err ) {
71
70
return nil , err
72
71
}
73
- // Get existing Service and return it
72
+ // Service already exists, we want to update it
73
+ // as we do not know if any fields might have changed.
74
74
existingService := & v1.Service {}
75
75
err := client .Get (ctx , types.NamespacedName {
76
76
Name : s .Name ,
77
77
Namespace : s .Namespace ,
78
78
}, existingService )
79
+
80
+ s .ResourceVersion = existingService .ResourceVersion
81
+ if existingService .Spec .Type == v1 .ServiceTypeClusterIP {
82
+ s .Spec .ClusterIP = existingService .Spec .ClusterIP
83
+ }
84
+ err = client .Update (ctx , s )
79
85
if err != nil {
80
86
return nil , err
81
87
}
82
- log .Info ("Metrics Service object already exists " , "name " , existingService .Name )
88
+ log .V ( 1 ). Info ("Metrics Service object updated " , "Service.Name " , s .Name , "Service.Namespace" , s . Namespace )
83
89
return existingService , nil
84
90
}
85
91
86
- log .Info ("Metrics Service object created" , "name " , s .Name )
92
+ log .Info ("Metrics Service object created" , "Service.Name " , s .Name , "Service.Namespace" , s . Namespace )
87
93
return s , nil
88
94
}
89
95
90
96
// initOperatorService returns the static service which exposes specifed port.
91
- func initOperatorService (port int32 , portName string ) (* v1.Service , error ) {
97
+ func initOperatorService (ctx context. Context , client crclient. Client , port int32 , portName string ) (* v1.Service , error ) {
92
98
operatorName , err := k8sutil .GetOperatorName ()
93
99
if err != nil {
94
100
return nil , err
@@ -97,11 +103,14 @@ func initOperatorService(port int32, portName string) (*v1.Service, error) {
97
103
if err != nil {
98
104
return nil , err
99
105
}
106
+
107
+ label := map [string ]string {"name" : operatorName }
108
+
100
109
service := & v1.Service {
101
110
ObjectMeta : metav1.ObjectMeta {
102
111
Name : operatorName ,
103
112
Namespace : namespace ,
104
- Labels : map [ string ] string { "name" : operatorName } ,
113
+ Labels : label ,
105
114
},
106
115
TypeMeta : metav1.TypeMeta {
107
116
Kind : "Service" ,
@@ -119,8 +128,72 @@ func initOperatorService(port int32, portName string) (*v1.Service, error) {
119
128
Name : portName ,
120
129
},
121
130
},
122
- Selector : map [ string ] string { "name" : operatorName } ,
131
+ Selector : label ,
123
132
},
124
133
}
134
+
135
+ ownRef , err := getPodOwnerRef (ctx , client , namespace )
136
+ if err != nil {
137
+ return nil , err
138
+ }
139
+ service .SetOwnerReferences ([]metav1.OwnerReference {* ownRef })
140
+
125
141
return service , nil
126
142
}
143
+
144
+ func getPodOwnerRef (ctx context.Context , client crclient.Client , ns string ) (* metav1.OwnerReference , error ) {
145
+ // Get current Pod the operator is running in
146
+ pod , err := k8sutil .GetPod (ctx , client , ns )
147
+ if err != nil {
148
+ return nil , err
149
+ }
150
+ podOwnerRefs := metav1 .NewControllerRef (pod , pod .GroupVersionKind ())
151
+ // Get Owner that the Pod belongs to
152
+ ownerRef := metav1 .GetControllerOf (pod )
153
+ finalOwnerRef , err := findFinalOwnerRef (ctx , client , ns , ownerRef )
154
+ if err != nil {
155
+ return nil , err
156
+ }
157
+ if finalOwnerRef != nil {
158
+ return finalOwnerRef , nil
159
+ }
160
+
161
+ // Default to returning Pod as the Owner
162
+ return podOwnerRefs , nil
163
+ }
164
+
165
+ // findFinalOwnerRef tries to locate the final controller/owner based on the owner reference provided.
166
+ func findFinalOwnerRef (ctx context.Context , client crclient.Client , ns string , ownerRef * metav1.OwnerReference ) (* metav1.OwnerReference , error ) {
167
+ if ownerRef == nil {
168
+ return nil , nil
169
+ }
170
+
171
+ obj := & unstructured.Unstructured {}
172
+ obj .SetAPIVersion (ownerRef .APIVersion )
173
+ obj .SetKind (ownerRef .Kind )
174
+ err := client .Get (ctx , types.NamespacedName {Namespace : ns , Name : ownerRef .Name }, obj )
175
+ if err != nil {
176
+ return nil , err
177
+ }
178
+ newOwnerRef := metav1 .GetControllerOf (obj )
179
+ if newOwnerRef != nil {
180
+ return findFinalOwnerRef (ctx , client , ns , newOwnerRef )
181
+ }
182
+
183
+ log .V (1 ).Info ("Pods owner found" , "Kind" , ownerRef .Kind , "Name" , ownerRef .Name , "Namespace" , ns )
184
+ return ownerRef , nil
185
+ }
186
+
187
+ func createClient () (crclient.Client , error ) {
188
+ config , err := config .GetConfig ()
189
+ if err != nil {
190
+ return nil , err
191
+ }
192
+
193
+ client , err := crclient .New (config , crclient.Options {})
194
+ if err != nil {
195
+ return nil , err
196
+ }
197
+
198
+ return client , nil
199
+ }
0 commit comments