|
| 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