Skip to content

Commit 2a0877e

Browse files
Lini KurienLiniSusan
Lini Kurien
authored andcommitted
ARO-13380 - metrics: cwp status
1 parent 3a0f4bd commit 2a0877e

File tree

5 files changed

+356
-10
lines changed

5 files changed

+356
-10
lines changed

pkg/monitor/cluster/cluster.go

+21-8
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import (
2121
configv1 "github.com/openshift/api/config/v1"
2222
configclient "github.com/openshift/client-go/config/clientset/versioned"
2323
machineclient "github.com/openshift/client-go/machine/clientset/versioned"
24+
operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
2425
mcoclient "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned"
2526

2627
"github.com/Azure/ARO-RP/pkg/api"
28+
"github.com/Azure/ARO-RP/pkg/env"
2729
"github.com/Azure/ARO-RP/pkg/hive"
2830
"github.com/Azure/ARO-RP/pkg/metrics"
2931
"github.com/Azure/ARO-RP/pkg/monitor/dimension"
@@ -43,13 +45,16 @@ type Monitor struct {
4345
oc *api.OpenShiftCluster
4446
dims map[string]string
4547

46-
restconfig *rest.Config
47-
cli kubernetes.Interface
48-
configcli configclient.Interface
49-
maocli machineclient.Interface
50-
mcocli mcoclient.Interface
51-
m metrics.Emitter
52-
arocli aroclient.Interface
48+
restconfig *rest.Config
49+
cli kubernetes.Interface
50+
configcli configclient.Interface
51+
operatorcli operatorclient.Interface
52+
maocli machineclient.Interface
53+
mcocli mcoclient.Interface
54+
m metrics.Emitter
55+
arocli aroclient.Interface
56+
env env.Interface
57+
tenantID string
5358

5459
ocpclientset client.Client
5560
hiveclientset client.Client
@@ -68,7 +73,7 @@ type Monitor struct {
6873
doc *api.OpenShiftClusterDocument
6974
}
7075

71-
func NewMonitor(log *logrus.Entry, restConfig *rest.Config, oc *api.OpenShiftCluster, doc *api.OpenShiftClusterDocument, m metrics.Emitter, hiveRestConfig *rest.Config, hourlyRun bool, wg *sync.WaitGroup, hiveClusterManager hive.ClusterManager) (*Monitor, error) {
76+
func NewMonitor(log *logrus.Entry, restConfig *rest.Config, oc *api.OpenShiftCluster, doc *api.OpenShiftClusterDocument, env env.Interface, tenantID string, m metrics.Emitter, hiveRestConfig *rest.Config, hourlyRun bool, wg *sync.WaitGroup, hiveClusterManager hive.ClusterManager) (*Monitor, error) {
7277
r, err := azure.ParseResourceID(oc.ID)
7378
if err != nil {
7479
return nil, err
@@ -105,6 +110,10 @@ func NewMonitor(log *logrus.Entry, restConfig *rest.Config, oc *api.OpenShiftClu
105110
if err != nil {
106111
return nil, err
107112
}
113+
operatorcli, err := operatorclient.NewForConfig(restConfig)
114+
if err != nil {
115+
return nil, err
116+
}
108117

109118
// lazy discovery will not attempt to reach out to the apiserver immediately
110119
mapper, err := apiutil.NewDynamicRESTMapper(restConfig, apiutil.WithLazyDiscovery)
@@ -134,9 +143,12 @@ func NewMonitor(log *logrus.Entry, restConfig *rest.Config, oc *api.OpenShiftClu
134143
restconfig: restConfig,
135144
cli: cli,
136145
configcli: configcli,
146+
operatorcli: operatorcli,
137147
maocli: maocli,
138148
mcocli: mcocli,
139149
arocli: arocli,
150+
env: env,
151+
tenantID: tenantID,
140152
m: m,
141153
ocpclientset: ocpclientset,
142154
hiveclientset: hiveclientset,
@@ -221,6 +233,7 @@ func (mon *Monitor) Monitor(ctx context.Context) (errs []error) {
221233
mon.emitCertificateExpirationStatuses,
222234
mon.emitEtcdCertificateExpiry,
223235
mon.emitPrometheusAlerts, // at the end for now because it's the slowest/least reliable
236+
mon.emitCWPStatus,
224237
} {
225238
err = f(ctx)
226239
if err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package cluster
2+
3+
// Copyright (c) Microsoft Corporation.
4+
// Licensed under the Apache License 2.0.
5+
6+
import (
7+
"context"
8+
"net/url"
9+
"strconv"
10+
"strings"
11+
12+
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2"
13+
"github.com/Azure/go-autorest/autorest/azure"
14+
"github.com/sirupsen/logrus"
15+
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
18+
apisubnet "github.com/Azure/ARO-RP/pkg/api/util/subnet"
19+
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
20+
)
21+
22+
const (
23+
cwp = "clusterWideProxy.status"
24+
cwpErrorMessage = "NoProxy entries are incorrect"
25+
cluster = "cluster"
26+
mandatory_no_proxies = "localhost,127.0.0.1,.svc,.cluster.local,169.254.169.254"
27+
AzureDNS = "168.63.129.16"
28+
//169.254.169.254 (the IMDS IP)
29+
//168.63.129.16 (Azure DNS, if no custom DNS exists)
30+
//localhost, 127.0.0.1, .svc, .cluster.local
31+
)
32+
33+
// Main function to emit CWP status
34+
func (mon *Monitor) emitCWPStatus(ctx context.Context) error {
35+
proxyConfig, err := mon.configcli.ConfigV1().Proxies().Get(ctx, cluster, metav1.GetOptions{})
36+
if err != nil {
37+
mon.log.Errorf("Error in getting the cluster wide proxy: %v", err)
38+
return err
39+
}
40+
if proxyConfig.Spec.HTTPProxy == "" && proxyConfig.Spec.HTTPSProxy == "" && proxyConfig.Spec.NoProxy == "" {
41+
mon.emitGauge(cwp, 1, map[string]string{
42+
"status": strconv.FormatBool(false),
43+
"Message": "CWP Not Enabled",
44+
})
45+
} else {
46+
// Create the noProxy map for efficient lookups
47+
no_proxy_list := strings.Split(proxyConfig.Spec.NoProxy, ",")
48+
noProxyMap := make(map[string]bool)
49+
var missing_no_proxy_list []string
50+
for _, proxy := range no_proxy_list {
51+
noProxyMap[proxy] = true
52+
}
53+
54+
// Check mandatory no_proxy entries
55+
for _, mandatory_no_proxy := range strings.Split(mandatory_no_proxies, ",") {
56+
if !noProxyMap[mandatory_no_proxy] {
57+
missing_no_proxy_list = append(missing_no_proxy_list, mandatory_no_proxy)
58+
}
59+
}
60+
if !noProxyMap[AzureDNS] {
61+
dnsConfigcluster, err := mon.operatorcli.OperatorV1().DNSes().Get(ctx, "default", metav1.GetOptions{})
62+
if err != nil {
63+
mon.log.Errorf("Error in getting DNS configuration: %v", err)
64+
return err
65+
}
66+
if len(dnsConfigcluster.Spec.Servers) == 0 {
67+
missing_no_proxy_list = append(missing_no_proxy_list, AzureDNS)
68+
}
69+
}
70+
71+
mastersubnetID, err := azure.ParseResourceID(mon.oc.Properties.MasterProfile.SubnetID)
72+
if err != nil {
73+
mon.log.Errorf("failed to parse the mastersubnetID: %v", err)
74+
return err
75+
}
76+
token, err := mon.env.FPNewClientCertificateCredential(mon.tenantID, nil)
77+
if err != nil {
78+
mon.log.Errorf("failed to obtain FP Client Credentials: %v", err)
79+
return err
80+
}
81+
82+
// Create client factory
83+
clientFactory, err := armnetwork.NewClientFactory(mastersubnetID.SubscriptionID, token, nil)
84+
if err != nil {
85+
mon.log.Errorf("failed to create client: %v", err)
86+
return err
87+
}
88+
89+
// Check master subnet
90+
masterVnetID, _, err := apisubnet.Split(mon.oc.Properties.MasterProfile.SubnetID)
91+
if err != nil {
92+
mon.log.Errorf("failed to get the masterVnetID: %v", err)
93+
return err
94+
}
95+
mastervnetId, err := azure.ParseResourceID(masterVnetID)
96+
if err != nil {
97+
mon.log.Errorf("failed to parse the masterVnetID: %v", err)
98+
return err
99+
}
100+
res, err := clientFactory.NewSubnetsClient().Get(ctx, mastersubnetID.ResourceGroup, mastervnetId.ResourceName, mastersubnetID.ResourceName, &armnetwork.SubnetsClientGetOptions{Expand: nil})
101+
if err != nil {
102+
mon.log.Errorf("failed to finish the NewSubnetsClient request: %v", err)
103+
return err
104+
}
105+
106+
if res.Properties.AddressPrefix != nil {
107+
if !noProxyMap[*res.Properties.AddressPrefix] {
108+
missing_no_proxy_list = append(missing_no_proxy_list, *res.Properties.AddressPrefix)
109+
}
110+
}
111+
112+
// Check worker profiles
113+
for _, workerProfile := range mon.oc.Properties.WorkerProfiles {
114+
workersubnetID, err := azure.ParseResourceID(workerProfile.SubnetID)
115+
if err != nil {
116+
mon.log.Errorf("failed to parse the workersubnetID: %v", err)
117+
return err
118+
}
119+
workerVnetID, _, err := apisubnet.Split(workerProfile.SubnetID)
120+
if err != nil {
121+
mon.log.Errorf("failed to feth the workerVnetID: %v", err)
122+
return err
123+
}
124+
workervnetId, err := azure.ParseResourceID(workerVnetID)
125+
if err != nil {
126+
mon.log.Errorf("failed to parse the workerVnetID: %v", err)
127+
return err
128+
}
129+
workerres, err := clientFactory.NewSubnetsClient().Get(ctx, workersubnetID.ResourceGroup, workervnetId.ResourceName, workersubnetID.ResourceName, &armnetwork.SubnetsClientGetOptions{Expand: nil})
130+
if err != nil {
131+
mon.log.Errorf("failed to finish the request: %v", err)
132+
}
133+
if workerres.Properties.AddressPrefix != nil {
134+
workermachinesCIDR := *workerres.Properties.AddressPrefix
135+
if !noProxyMap[workermachinesCIDR] {
136+
missing_no_proxy_list = append(missing_no_proxy_list, workermachinesCIDR)
137+
}
138+
}
139+
}
140+
141+
// Network Configuration Check
142+
networkConfig, err := mon.configcli.ConfigV1().Networks().Get(ctx, cluster, metav1.GetOptions{})
143+
if err != nil {
144+
mon.log.Errorf("Error in getting network info: %v", err)
145+
return err
146+
}
147+
for _, network := range networkConfig.Spec.ClusterNetwork {
148+
if !noProxyMap[network.CIDR] {
149+
missing_no_proxy_list = append(missing_no_proxy_list, network.CIDR)
150+
}
151+
}
152+
for _, network := range networkConfig.Spec.ServiceNetwork {
153+
if !noProxyMap[network] {
154+
missing_no_proxy_list = append(missing_no_proxy_list, network)
155+
}
156+
}
157+
158+
// Gateway Domains Check
159+
clusterdetails, err := mon.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
160+
if err != nil {
161+
mon.log.Errorf("Error in getting cluster information: %v", err)
162+
return err
163+
}
164+
clusterDomain := clusterdetails.Spec.Domain
165+
if !(noProxyMap[clusterDomain] || noProxyMap[".apps."+clusterDomain] || noProxyMap["."+clusterDomain]) {
166+
missing_no_proxy_list = append(missing_no_proxy_list, clusterDomain)
167+
}
168+
for _, gatewayDomain := range clusterdetails.Spec.GatewayDomains {
169+
gatewayDomain = strings.ToLower(gatewayDomain)
170+
if !noProxyMap[gatewayDomain] {
171+
missing_no_proxy_list = append(missing_no_proxy_list, gatewayDomain)
172+
}
173+
}
174+
175+
// Infrastructure Configuration Check
176+
infraConfig, err := mon.configcli.ConfigV1().Infrastructures().Get(ctx, cluster, metav1.GetOptions{})
177+
if err != nil {
178+
mon.log.Errorf("Error in getting Infrasturcture info: %v", err)
179+
return err
180+
}
181+
182+
// APIServerInternal URL Check
183+
apiServerIntURL, err := url.Parse(infraConfig.Status.APIServerInternalURL)
184+
if err != nil {
185+
mon.log.Errorf("Error in parsing APIServerProfile: %v", err)
186+
return err
187+
}
188+
apiServerIntdomain := strings.Split(apiServerIntURL.Host, ":")[0]
189+
if !noProxyMap[apiServerIntdomain] {
190+
missing_no_proxy_list = append(missing_no_proxy_list, apiServerIntdomain)
191+
}
192+
193+
// APIServerProfile URL Check
194+
apiServerProfileURL, err := url.Parse(mon.oc.Properties.APIServerProfile.URL)
195+
if err != nil {
196+
mon.log.Errorf("Error in parsing APIServerProfile: %v", err)
197+
return err
198+
}
199+
apiServerProfiledomain := strings.Split(apiServerProfileURL.Host, ":")[0]
200+
if !noProxyMap[apiServerProfiledomain] {
201+
missing_no_proxy_list = append(missing_no_proxy_list, apiServerProfiledomain)
202+
}
203+
204+
if len(missing_no_proxy_list) > 0 {
205+
status := true
206+
message := "CWP enabled but missing " + strings.Join(missing_no_proxy_list, ",") + " in the no_proxy list"
207+
mon.emitGauge(cwp, 1, map[string]string{
208+
"status": strconv.FormatBool(status),
209+
"Message": message,
210+
})
211+
mon.log.Infof(message)
212+
if mon.hourlyRun {
213+
mon.log.WithFields(logrus.Fields{
214+
"metric": cwp,
215+
"status": strconv.FormatBool(status),
216+
"Message": message,
217+
}).Print()
218+
}
219+
} else {
220+
mon.emitGauge(cwp, 1, map[string]string{
221+
"status": strconv.FormatBool(false),
222+
"Message": "CWP enabled successfully",
223+
})
224+
mon.log.Infof("CWP enabled successfully")
225+
}
226+
}
227+
228+
return nil
229+
}

0 commit comments

Comments
 (0)