|
| 1 | +// Copyright The OpenTelemetry Authors |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +// TODO remove this package in favor of the new arriving feature in Azure SDK for Go. |
| 5 | +// Ref: https://github.com/Azure/azure-sdk-for-go/pull/24309 |
| 6 | + |
| 7 | +package fake // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azuremonitorreceiver/fake" |
| 8 | + |
| 9 | +import ( |
| 10 | + "context" |
| 11 | + "errors" |
| 12 | + "fmt" |
| 13 | + "net/http" |
| 14 | + "net/url" |
| 15 | + "regexp" |
| 16 | + "strconv" |
| 17 | + "strings" |
| 18 | + |
| 19 | + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" |
| 20 | + "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake/server" |
| 21 | + "github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics" |
| 22 | +) |
| 23 | + |
| 24 | +// MetricsServer is a fake server for instances of the azmetrics.MetricsClient type. |
| 25 | +type MetricsServer struct { |
| 26 | + // QueryResources is the fake for method MetricsClient.List |
| 27 | + // HTTP status codes to indicate success: http.StatusOK |
| 28 | + QueryResources func(ctx context.Context, subscriptionID, metricNamespace string, metricNames []string, resourceIDs azmetrics.ResourceIDList, options *azmetrics.QueryResourcesOptions) (resp azfake.Responder[azmetrics.QueryResourcesResponse], errResp azfake.ErrorResponder) |
| 29 | +} |
| 30 | + |
| 31 | +// NewMetricsServerTransport creates a new instance of MetricsServerTransport with the provided implementation. |
| 32 | +// The returned MetricsServerTransport instance is connected to an instance of azmetrics.MetricsClient via the |
| 33 | +// azcore.ClientOptions.Transporter field in the client's constructor parameters. |
| 34 | +func NewMetricsServerTransport(srv *MetricsServer) *MetricsServerTransport { |
| 35 | + return &MetricsServerTransport{srv: srv} |
| 36 | +} |
| 37 | + |
| 38 | +// MetricsServerTransport connects instances of armmonitor.MetricsClient to instances of MetricsServer. |
| 39 | +// Don't use this type directly, use NewMetricsServerTransport instead. |
| 40 | +type MetricsServerTransport struct { |
| 41 | + srv *MetricsServer |
| 42 | +} |
| 43 | + |
| 44 | +// Do implements the policy.Transporter interface for MetricsServerTransport. |
| 45 | +func (m *MetricsServerTransport) Do(req *http.Request) (*http.Response, error) { |
| 46 | + // rawMethod := req.Context().Value(runtime.CtxAPINameKey{}) |
| 47 | + // method, ok := rawMethod.(string) |
| 48 | + // if !ok { |
| 49 | + // return nil, nonRetriableError{errors.New("unable to dispatch request, missing value for CtxAPINameKey")} |
| 50 | + // } |
| 51 | + // |
| 52 | + // var resp *http.Response |
| 53 | + // var err error |
| 54 | + // |
| 55 | + // switch method { |
| 56 | + // case "Client.QueryResources": |
| 57 | + // resp, err = m.dispatchQueryResources(req) |
| 58 | + // default: |
| 59 | + // err = fmt.Errorf("unhandled API %s", method) |
| 60 | + // } |
| 61 | + // |
| 62 | + // if err != nil { |
| 63 | + // return nil, err |
| 64 | + // } |
| 65 | + // |
| 66 | + // return resp, nil |
| 67 | + |
| 68 | + // We can't directly reuse the logic as the runtime.CtxAPINameKey is not send by the client. |
| 69 | + return m.dispatchQueryResources(req) |
| 70 | +} |
| 71 | + |
| 72 | +func (m *MetricsServerTransport) dispatchQueryResources(req *http.Request) (*http.Response, error) { |
| 73 | + if m.srv.QueryResources == nil { |
| 74 | + return nil, &nonRetriableError{errors.New("fake for method QueryResources not implemented")} |
| 75 | + } |
| 76 | + |
| 77 | + const regexStr = `/subscriptions/(?P<subscriptionId>[!#&$-;=?-\[\]_a-zA-Z0-9~%@]+)/metrics:getBatch` |
| 78 | + regex := regexp.MustCompile(regexStr) |
| 79 | + matches := regex.FindStringSubmatch(req.URL.EscapedPath()) |
| 80 | + if len(matches) < 1 { |
| 81 | + return nil, fmt.Errorf("failed to parse path %s", req.URL.Path) |
| 82 | + } |
| 83 | + qp := req.URL.Query() |
| 84 | + body, err := server.UnmarshalRequestAsJSON[azmetrics.ResourceIDList](req) |
| 85 | + if err != nil { |
| 86 | + return nil, err |
| 87 | + } |
| 88 | + subscriptionIDParam, err := url.PathUnescape(matches[regex.SubexpIndex("subscriptionId")]) |
| 89 | + if err != nil { |
| 90 | + return nil, err |
| 91 | + } |
| 92 | + startTimeUnescaped, err := url.QueryUnescape(qp.Get("startTime")) |
| 93 | + if err != nil { |
| 94 | + return nil, err |
| 95 | + } |
| 96 | + startTimeParam := getOptional(startTimeUnescaped) |
| 97 | + endTimeUnescaped, err := url.QueryUnescape(qp.Get("endTime")) |
| 98 | + if err != nil { |
| 99 | + return nil, err |
| 100 | + } |
| 101 | + endTimeParam := getOptional(endTimeUnescaped) |
| 102 | + intervalUnescaped, err := url.QueryUnescape(qp.Get("interval")) |
| 103 | + if err != nil { |
| 104 | + return nil, err |
| 105 | + } |
| 106 | + intervalParam := getOptional(intervalUnescaped) |
| 107 | + metricnamesUnescaped, err := url.QueryUnescape(qp.Get("metricnames")) |
| 108 | + if err != nil { |
| 109 | + return nil, err |
| 110 | + } |
| 111 | + aggregationUnescaped, err := url.QueryUnescape(qp.Get("aggregation")) |
| 112 | + if err != nil { |
| 113 | + return nil, err |
| 114 | + } |
| 115 | + aggregationParam := getOptional(aggregationUnescaped) |
| 116 | + topUnescaped, err := url.QueryUnescape(qp.Get("top")) |
| 117 | + if err != nil { |
| 118 | + return nil, err |
| 119 | + } |
| 120 | + topParam, err := parseOptional(topUnescaped, func(v string) (int32, error) { |
| 121 | + p, parseErr := strconv.ParseInt(v, 10, 32) |
| 122 | + if parseErr != nil { |
| 123 | + return 0, parseErr |
| 124 | + } |
| 125 | + return int32(p), nil |
| 126 | + }) |
| 127 | + if err != nil { |
| 128 | + return nil, err |
| 129 | + } |
| 130 | + orderbyUnescaped, err := url.QueryUnescape(qp.Get("orderby")) |
| 131 | + if err != nil { |
| 132 | + return nil, err |
| 133 | + } |
| 134 | + orderbyParam := getOptional(orderbyUnescaped) |
| 135 | + rollupbyUnescaped, err := url.QueryUnescape(qp.Get("rollupby")) |
| 136 | + if err != nil { |
| 137 | + return nil, err |
| 138 | + } |
| 139 | + rollupbybyParam := getOptional(rollupbyUnescaped) |
| 140 | + filterUnescaped, err := url.QueryUnescape(qp.Get("filter")) |
| 141 | + if err != nil { |
| 142 | + return nil, err |
| 143 | + } |
| 144 | + filterParam := getOptional(filterUnescaped) |
| 145 | + metricnamespaceUnescaped, err := url.QueryUnescape(qp.Get("metricnamespace")) |
| 146 | + if err != nil { |
| 147 | + return nil, err |
| 148 | + } |
| 149 | + var options *azmetrics.QueryResourcesOptions |
| 150 | + if startTimeParam != nil || endTimeParam != nil || intervalParam != nil || aggregationParam != nil || topParam != nil || orderbyParam != nil || rollupbybyParam != nil || filterParam != nil { |
| 151 | + options = &azmetrics.QueryResourcesOptions{ |
| 152 | + StartTime: startTimeParam, |
| 153 | + EndTime: endTimeParam, |
| 154 | + Interval: intervalParam, |
| 155 | + Aggregation: aggregationParam, |
| 156 | + Top: topParam, |
| 157 | + OrderBy: orderbyParam, |
| 158 | + RollUpBy: rollupbybyParam, |
| 159 | + Filter: filterParam, |
| 160 | + } |
| 161 | + } |
| 162 | + respr, errRespr := m.srv.QueryResources(req.Context(), subscriptionIDParam, metricnamespaceUnescaped, strings.Split(metricnamesUnescaped, ","), body, options) |
| 163 | + if respErr := server.GetError(errRespr, req); respErr != nil { |
| 164 | + return nil, respErr |
| 165 | + } |
| 166 | + respContent := server.GetResponseContent(respr) |
| 167 | + if !contains([]int{http.StatusOK}, respContent.HTTPStatus) { |
| 168 | + return nil, &nonRetriableError{fmt.Errorf("unexpected status code %d. acceptable values are http.StatusOK", respContent.HTTPStatus)} |
| 169 | + } |
| 170 | + resp, err := server.MarshalResponseAsJSON(respContent, server.GetResponse(respr), req) |
| 171 | + if err != nil { |
| 172 | + return nil, err |
| 173 | + } |
| 174 | + return resp, nil |
| 175 | +} |
0 commit comments