1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00
kyverno/pkg/cel/libs/http/http.go
Frank Jogeleit 1a7c581220
fix: providing the http provider in the compiler (#12379)
Signed-off-by: Frank Jogeleit <frank.jogeleit@web.de>
2025-03-11 15:00:20 +00:00

119 lines
2.9 KiB
Go

package http
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/kyverno/kyverno/pkg/tracing"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
type HttpInterface interface {
Get(url string, headers map[string]string) (map[string]any, error)
Post(url string, data map[string]any, headers map[string]string) (map[string]any, error)
Client(caBundle string) (HttpInterface, error)
}
type ClientInterface interface {
Do(*http.Request) (*http.Response, error)
}
type HTTP struct {
HttpInterface
}
type HttpProvider struct {
client ClientInterface
}
func (r *HttpProvider) Get(url string, headers map[string]string) (map[string]any, error) {
req, err := http.NewRequestWithContext(context.TODO(), "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
}
for h, v := range headers {
req.Header.Add(h, v)
}
return r.executeRequest(r.client, req)
}
func (r *HttpProvider) Post(url string, data map[string]any, headers map[string]string) (map[string]any, error) {
body, err := buildRequestData(data)
if err != nil {
return nil, fmt.Errorf("failed to encode request data: %v", err)
}
req, err := http.NewRequestWithContext(context.TODO(), "POST", url, body)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
}
for h, v := range headers {
req.Header.Add(h, v)
}
return r.executeRequest(r.client, req)
}
func (r *HttpProvider) executeRequest(client ClientInterface, req *http.Request) (map[string]any, error) {
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("HTTP %s", resp.Status)
}
body := make(map[string]any)
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, fmt.Errorf("Unable to decode JSON body %v", err)
}
return body, nil
}
func (r *HttpProvider) Client(caBundle string) (HttpInterface, error) {
if caBundle == "" {
return r, nil
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM([]byte(caBundle)); !ok {
return nil, fmt.Errorf("failed to parse PEM CA bundle for APICall")
}
transport := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
},
}
return &HttpProvider{
client: &http.Client{
Transport: tracing.Transport(transport, otelhttp.WithFilter(tracing.RequestFilterIsInSpan)),
},
}, nil
}
func buildRequestData(data map[string]any) (io.Reader, error) {
buffer := new(bytes.Buffer)
if err := json.NewEncoder(buffer).Encode(data); err != nil {
return nil, fmt.Errorf("failed to encode HTTP POST data %v: %w", data, err)
}
return buffer, nil
}
func NewHTTP() HTTP {
return HTTP{
HttpInterface: &HttpProvider{
client: http.DefaultClient,
},
}
}