@@ -25,22 +25,6 @@ import (
25
25
"strings"
26
26
"time"
27
27
28
- "github.com/imdario/mergo"
29
- configv1 "github.com/openshift/api/config/v1"
30
- consolev1 "github.com/openshift/api/console/v1"
31
- osmv1 "github.com/openshift/api/monitoring/v1"
32
- routev1 "github.com/openshift/api/route/v1"
33
- secv1 "github.com/openshift/api/security/v1"
34
- openshiftconfigclientset "github.com/openshift/client-go/config/clientset/versioned"
35
- openshiftconsoleclientset "github.com/openshift/client-go/console/clientset/versioned"
36
- openshiftmonitoringclientset "github.com/openshift/client-go/monitoring/clientset/versioned"
37
- openshiftoperatorclientset "github.com/openshift/client-go/operator/clientset/versioned"
38
- openshiftrouteclientset "github.com/openshift/client-go/route/clientset/versioned"
39
- openshiftsecurityclientset "github.com/openshift/client-go/security/clientset/versioned"
40
- "github.com/openshift/library-go/pkg/operator/events"
41
- "github.com/openshift/library-go/pkg/operator/resource/resourceapply"
42
- monv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
43
- monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
44
28
admissionv1 "k8s.io/api/admissionregistration/v1"
45
29
appsv1 "k8s.io/api/apps/v1"
46
30
v1 "k8s.io/api/core/v1"
@@ -50,6 +34,7 @@ import (
50
34
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
51
35
apierrors "k8s.io/apimachinery/pkg/api/errors"
52
36
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
53
38
"k8s.io/apimachinery/pkg/fields"
54
39
"k8s.io/apimachinery/pkg/runtime"
55
40
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -58,6 +43,7 @@ import (
58
43
"k8s.io/apimachinery/pkg/util/intstr"
59
44
"k8s.io/apimachinery/pkg/util/wait"
60
45
"k8s.io/apimachinery/pkg/watch"
46
+ "k8s.io/client-go/dynamic"
61
47
"k8s.io/client-go/kubernetes"
62
48
"k8s.io/client-go/metadata"
63
49
"k8s.io/client-go/rest"
@@ -66,6 +52,23 @@ import (
66
52
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
67
53
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
68
54
"k8s.io/utils/ptr"
55
+
56
+ "github.com/imdario/mergo"
57
+ configv1 "github.com/openshift/api/config/v1"
58
+ consolev1 "github.com/openshift/api/console/v1"
59
+ osmv1 "github.com/openshift/api/monitoring/v1"
60
+ routev1 "github.com/openshift/api/route/v1"
61
+ secv1 "github.com/openshift/api/security/v1"
62
+ openshiftconfigclientset "github.com/openshift/client-go/config/clientset/versioned"
63
+ openshiftconsoleclientset "github.com/openshift/client-go/console/clientset/versioned"
64
+ openshiftmonitoringclientset "github.com/openshift/client-go/monitoring/clientset/versioned"
65
+ openshiftoperatorclientset "github.com/openshift/client-go/operator/clientset/versioned"
66
+ openshiftrouteclientset "github.com/openshift/client-go/route/clientset/versioned"
67
+ openshiftsecurityclientset "github.com/openshift/client-go/security/clientset/versioned"
68
+ "github.com/openshift/library-go/pkg/operator/events"
69
+ "github.com/openshift/library-go/pkg/operator/resource/resourceapply"
70
+ monv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
71
+ monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned"
69
72
)
70
73
71
74
const (
@@ -81,6 +84,7 @@ type Client struct {
81
84
userWorkloadNamespace string
82
85
83
86
kclient kubernetes.Interface
87
+ dclient dynamic.Interface
84
88
mdataclient metadata.Interface
85
89
osmclient openshiftmonitoringclientset.Interface
86
90
oscclient openshiftconfigclientset.Interface
@@ -107,6 +111,14 @@ func NewForConfig(cfg *rest.Config, version string, namespace, userWorkloadNames
107
111
client .kclient = kclient
108
112
}
109
113
114
+ if client .dclient == nil {
115
+ dclient , err := dynamic .NewForConfig (cfg )
116
+ if err != nil {
117
+ return nil , fmt .Errorf ("creating dynamic clientset client: %w" , err )
118
+ }
119
+ client .dclient = dclient
120
+ }
121
+
110
122
if client .eclient == nil {
111
123
eclient , err := apiextensionsclient .NewForConfig (cfg )
112
124
if err != nil {
@@ -203,6 +215,12 @@ func KubernetesClient(kclient kubernetes.Interface) Option {
203
215
}
204
216
}
205
217
218
+ func DynamicClient (dclient * dynamic.DynamicClient ) Option {
219
+ return func (c * Client ) {
220
+ c .dclient = dclient
221
+ }
222
+ }
223
+
206
224
func OpenshiftMonitoringClient (osmclient openshiftmonitoringclientset.Interface ) Option {
207
225
return func (c * Client ) {
208
226
c .osmclient = osmclient
@@ -632,29 +650,96 @@ func (c *Client) GetAlertingRule(ctx context.Context, namespace, name string) (*
632
650
return c .osmclient .MonitoringV1 ().AlertingRules (namespace ).Get (ctx , name , metav1.GetOptions {})
633
651
}
634
652
635
- func (c * Client ) CreateOrUpdatePrometheus (ctx context.Context , p * monv1.Prometheus ) error {
636
- pclient := c .mclient .MonitoringV1 ().Prometheuses (p .GetNamespace ())
637
- existing , err := pclient .Get (ctx , p .GetName (), metav1.GetOptions {})
653
+ func (c * Client ) CreateOrUpdatePrometheus (ctx context.Context , structuredRequiredPrometheus * monv1.Prometheus ) (* bool , error ) {
654
+ unstructuredRequiredPrometheusObject , err := runtime .DefaultUnstructuredConverter .ToUnstructured (structuredRequiredPrometheus )
655
+ if err != nil {
656
+ return nil , fmt .Errorf ("converting Prometheus object to unstructured failed: %w" , err )
657
+ }
658
+ unstructuredRequiredPrometheus := & unstructured.Unstructured {}
659
+ unstructuredRequiredPrometheus .SetUnstructuredContent (unstructuredRequiredPrometheusObject )
660
+
661
+ // Preserve the original behavior: always merge the metadata, never replace.
662
+ // Refer: https://github.com/openshift/cluster-monitoring-operator/pull/942.
663
+ unstructuredExistingPrometheus , err := c .dclient .Resource (structuredRequiredPrometheus .GroupVersionKind ().GroupVersion ().WithResource ("prometheuses" )).Namespace (structuredRequiredPrometheus .GetNamespace ()).Get (ctx , structuredRequiredPrometheus .GetName (), metav1.GetOptions {})
638
664
if apierrors .IsNotFound (err ) {
639
- _ , err := pclient . Create (ctx , p , metav1.CreateOptions {})
665
+ _ , err := c . dclient . Resource ( structuredRequiredPrometheus . GroupVersionKind (). GroupVersion (). WithResource ( "prometheuses" )). Namespace ( structuredRequiredPrometheus . GetNamespace ()). Create (ctx , unstructuredRequiredPrometheus , metav1.CreateOptions {})
640
666
if err != nil {
641
- return fmt .Errorf ("creating Prometheus object failed: %w" , err )
667
+ return nil , fmt .Errorf ("creating Prometheus object failed: %w" , err )
642
668
}
643
- return nil
669
+ return ptr . To ( true ), nil
644
670
}
645
671
if err != nil {
646
- return fmt .Errorf ("retrieving Prometheus object failed: %w" , err )
672
+ return nil , fmt .Errorf ("retrieving Prometheus object failed: %w" , err )
647
673
}
674
+ unstructuredRequiredPrometheusMetadataLabels := unstructuredRequiredPrometheus .GetLabels ()
675
+ unstructuredRequiredPrometheusMetadataAnnotations := unstructuredRequiredPrometheus .GetAnnotations ()
676
+ mergeMetadataLabels (unstructuredRequiredPrometheusMetadataLabels , unstructuredExistingPrometheus .GetLabels ())
677
+ mergeMetadataAnnotations (unstructuredRequiredPrometheusMetadataAnnotations , unstructuredExistingPrometheus .GetAnnotations ())
678
+ unstructuredRequiredPrometheus .SetLabels (unstructuredRequiredPrometheusMetadataLabels )
679
+ unstructuredRequiredPrometheus .SetAnnotations (unstructuredRequiredPrometheusMetadataAnnotations )
680
+ unstructuredRequiredPrometheus .SetResourceVersion (unstructuredExistingPrometheus .GetResourceVersion ())
648
681
649
- required := p .DeepCopy ()
650
- mergeMetadata (& required .ObjectMeta , existing .ObjectMeta )
682
+ _ , didUpdate , err := resourceapply .ApplyUnstructuredResourceImproved (
683
+ ctx ,
684
+ c .dclient ,
685
+ c .eventRecorder ,
686
+ unstructuredRequiredPrometheus ,
687
+ c .resourceCache ,
688
+ structuredRequiredPrometheus .GroupVersionKind ().GroupVersion ().WithResource ("prometheuses" ),
689
+ prometheusDefaultingFunc ,
690
+ nil ,
691
+ )
692
+ if err != nil {
693
+ return & didUpdate , fmt .Errorf ("updating Prometheus object failed: %w" , err )
694
+ }
651
695
652
- required .ResourceVersion = existing .ResourceVersion
653
- _ , err = pclient .Update (ctx , required , metav1.UpdateOptions {})
696
+ return & didUpdate , nil
697
+ }
698
+
699
+ func prometheusDefaultingFunc (unstructuredPrometheus * unstructured.Unstructured ) {
700
+ // Cast to the corresponding structured representation.
701
+ structuredPrometheus := & monv1.Prometheus {}
702
+ if err := runtime .DefaultUnstructuredConverter .FromUnstructured (unstructuredPrometheus .UnstructuredContent (), structuredPrometheus ); err != nil {
703
+ klog .ErrorS (err , "failed to convert unstructured to structured Prometheus spec" )
704
+ return
705
+ }
706
+
707
+ // Set defaults.
708
+ if structuredPrometheus .Spec .CommonPrometheusFields .ScrapeInterval == "" {
709
+ structuredPrometheus .Spec .CommonPrometheusFields .ScrapeInterval = "30s"
710
+ }
711
+ if len (structuredPrometheus .Spec .CommonPrometheusFields .ExternalLabels ) == 0 {
712
+ structuredPrometheus .Spec .CommonPrometheusFields .ExternalLabels = nil
713
+ }
714
+ if len (structuredPrometheus .Spec .CommonPrometheusFields .EnableFeatures ) == 0 {
715
+ structuredPrometheus .Spec .CommonPrometheusFields .EnableFeatures = nil
716
+ }
717
+ for i , container := range structuredPrometheus .Spec .CommonPrometheusFields .Containers {
718
+ for j , port := range container .Ports {
719
+ if port .Protocol == "" {
720
+ structuredPrometheus .Spec .CommonPrometheusFields .Containers [i ].Ports [j ].Protocol = "TCP"
721
+ }
722
+ }
723
+ }
724
+ if structuredPrometheus .Spec .CommonPrometheusFields .PortName == "" {
725
+ structuredPrometheus .Spec .CommonPrometheusFields .PortName = "web"
726
+ }
727
+ if structuredPrometheus .Spec .Thanos == nil {
728
+ structuredPrometheus .Spec .Thanos = & monv1.ThanosSpec {}
729
+ }
730
+ if structuredPrometheus .Spec .Thanos .BlockDuration == "" {
731
+ structuredPrometheus .Spec .Thanos .BlockDuration = "2h"
732
+ }
733
+ if structuredPrometheus .Spec .EvaluationInterval == "" {
734
+ structuredPrometheus .Spec .EvaluationInterval = "30s"
735
+ }
736
+
737
+ // Convert back to the corresponding unstructured representation and inject.
738
+ var err error
739
+ unstructuredPrometheus .Object , err = runtime .DefaultUnstructuredConverter .ToUnstructured (structuredPrometheus )
654
740
if err != nil {
655
- return fmt . Errorf ( "updating Prometheus object failed: %w" , err )
741
+ klog . ErrorS ( err , "failed to convert structured to unstructured Prometheus" )
656
742
}
657
- return nil
658
743
}
659
744
660
745
func (c * Client ) CreateOrUpdatePrometheusRule (ctx context.Context , p * monv1.PrometheusRule ) error {
@@ -1788,20 +1873,28 @@ func (c *Client) VPACustomResourceDefinitionPresent(ctx context.Context, lastKno
1788
1873
// where keys starting from string defined in `metadataPrefix` are deleted. This prevents issues with preserving stale
1789
1874
// metadata defined by the operator
1790
1875
func mergeMetadata (required * metav1.ObjectMeta , existing metav1.ObjectMeta ) {
1791
- for k := range existing .Annotations {
1876
+ mergeMetadataLabels (required .Labels , existing .Labels )
1877
+ mergeMetadataAnnotations (required .Annotations , existing .Annotations )
1878
+ }
1879
+
1880
+ func mergeMetadataLabels (requiredLabels map [string ]string , existingLabels map [string ]string ) {
1881
+ for k := range existingLabels {
1792
1882
if strings .HasPrefix (k , metadataPrefix ) {
1793
- delete (existing . Annotations , k )
1883
+ delete (existingLabels , k )
1794
1884
}
1795
1885
}
1796
1886
1797
- for k := range existing .Labels {
1887
+ _ = mergo .Merge (& requiredLabels , existingLabels )
1888
+ }
1889
+
1890
+ func mergeMetadataAnnotations (requiredAnnotations map [string ]string , existingAnnotations map [string ]string ) {
1891
+ for k := range existingAnnotations {
1798
1892
if strings .HasPrefix (k , metadataPrefix ) {
1799
- delete (existing . Labels , k )
1893
+ delete (existingAnnotations , k )
1800
1894
}
1801
1895
}
1802
1896
1803
- _ = mergo .Merge (& required .Annotations , existing .Annotations )
1804
- _ = mergo .Merge (& required .Labels , existing .Labels )
1897
+ _ = mergo .Merge (& requiredAnnotations , existingAnnotations )
1805
1898
}
1806
1899
1807
1900
type jsonPatch struct {
0 commit comments