Skip to content

Commit

Permalink
feat(client): add support for custom HTTP headers (#1756)
Browse files Browse the repository at this point in the history
* feat(grafana.spec.client): add generic headers section

Signed-off-by: christophe.boucharlat <[email protected]>

* feat(instrumentedRoundTripper): add methods to add headers in the instrumented Transport object and reuse it in subsequent requests

Signed-off-by: christophe.boucharlat <[email protected]>

* fix(grafana.spec.client): changed headers format to a map of key value to simplify syntax

Signed-off-by: christophe.boucharlat <[email protected]>

* fix(controllers/client/round_tripper): added checks on headers if nil, removed useless pointers & follow coding style

Signed-off-by: christophe.boucharlat <[email protected]>

* fix: regenerate code

* fix(client): nil point dereference

* chore: suppress false positives for errcheck linter

---------

Signed-off-by: christophe.boucharlat <[email protected]>
Co-authored-by: Igor Beliakov <[email protected]>
Co-authored-by: Igor Beliakov <[email protected]>
  • Loading branch information
3 people authored Nov 18, 2024
1 parent 1ef7aab commit 73ef281
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 13 deletions.
9 changes: 6 additions & 3 deletions api/v1beta1/grafana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ type GrafanaClient struct {
// TLS Configuration used to talk with the grafana instance.
// +optional
TLS *TLSConfig `json:"tls,omitempty"`
// Custom HTTP headers to use when interacting with this Grafana.
// +optional
Headers map[string]string `json:"headers,omitempty"`
}

// GrafanaPreferences holds Grafana preferences API settings
Expand All @@ -138,8 +141,8 @@ type GrafanaStatus struct {
Version string `json:"version,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// Grafana is the Schema for the grafanas API
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description=""
Expand All @@ -154,7 +157,7 @@ type Grafana struct {
Status GrafanaStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true
// +kubebuilder:object:root=true

// GrafanaList contains a list of Grafana
type GrafanaList struct {
Expand Down
7 changes: 7 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions config/crd/bases/grafana.integreatly.org_grafanas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
additionalProperties:
type: string
description: Custom HTTP headers to use when interacting with
this Grafana.
type: object
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
4 changes: 4 additions & 0 deletions controllers/client/grafana_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ func NewGeneratedGrafanaClient(ctx context.Context, c client.Client, grafana *v1
}

transport := NewInstrumentedRoundTripper(grafana.Name, metrics.GrafanaApiRequests, grafana.IsExternal(), tlsConfig)
if grafana.Spec.Client != nil && grafana.Spec.Client.Headers != nil {
transport.(*instrumentedRoundTripper).addHeaders(grafana.Spec.Client.Headers) //nolint:errcheck
}

client := &http.Client{
Transport: transport,
Expand All @@ -161,6 +164,7 @@ func NewGeneratedGrafanaClient(ctx context.Context, c client.Client, grafana *v1
if credentials.username != "" {
cfg.BasicAuth = url.UserPassword(credentials.username, credentials.password)
}

cl := genapi.NewHTTPClientWithConfig(nil, cfg)

return cl, nil
Expand Down
7 changes: 6 additions & 1 deletion controllers/client/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ func NewHTTPClient(ctx context.Context, c client.Client, grafana *v1beta1.Grafan
return nil, err
}

transport := NewInstrumentedRoundTripper(grafana.Name, metrics.GrafanaApiRequests, grafana.IsExternal(), tlsConfig)
if grafana.Spec.Client != nil && grafana.Spec.Client.Headers != nil {
transport.(*instrumentedRoundTripper).addHeaders(grafana.Spec.Client.Headers) //nolint:errcheck
}

return &http.Client{
Transport: NewInstrumentedRoundTripper(grafana.Name, metrics.GrafanaApiRequests, grafana.IsExternal(), tlsConfig),
Transport: transport,
Timeout: time.Second * timeout,
}, nil
}
26 changes: 25 additions & 1 deletion controllers/client/round_tripper.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type instrumentedRoundTripper struct {
relatedResource string
wrapped http.RoundTripper
metric *prometheus.CounterVec
headers map[string]string
}

func NewInstrumentedRoundTripper(relatedResource string, metric *prometheus.CounterVec, useProxy bool, tlsConfig *tls.Config) http.RoundTripper {
Expand All @@ -29,15 +30,24 @@ func NewInstrumentedRoundTripper(relatedResource string, metric *prometheus.Coun
transport.Proxy = nil
}

headers := make(map[string]string)
headers["user-agent"] = "grafana-operator/" + embeds.Version

return &instrumentedRoundTripper{
relatedResource: relatedResource,
wrapped: transport,
metric: metric,
headers: headers,
}
}

func (in *instrumentedRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Add("user-agent", "grafana-operator/"+embeds.Version)
if in.headers != nil {
for k, v := range in.headers {
r.Header.Add(k, v)
}
}

resp, err := in.wrapped.RoundTrip(r)
if resp != nil {
in.metric.WithLabelValues(
Expand All @@ -48,3 +58,17 @@ func (in *instrumentedRoundTripper) RoundTrip(r *http.Request) (*http.Response,
}
return resp, err
}

func (in *instrumentedRoundTripper) addHeaders(headers map[string]string) {
if headers == nil {
return
}

if in.headers == nil {
in.headers = make(map[string]string)
}

for k, v := range headers {
in.headers[k] = v
}
}
16 changes: 8 additions & 8 deletions controllers/grafana_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ type GrafanaReconciler struct {
IsOpenShift bool
}

//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas/finalizers,verbs=update
//+kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=get;list;create;update;delete;watch
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;patch
//+kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts;services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanas/finalizers,verbs=update
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=get;list;create;update;delete;watch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;patch
// +kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts;services;persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete

func (r *GrafanaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
controllerLog := log.FromContext(ctx).WithName("GrafanaReconciler")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
additionalProperties:
type: string
description: Custom HTTP headers to use when interacting with
this Grafana.
type: object
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
6 changes: 6 additions & 0 deletions deploy/kustomize/base/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,12 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
additionalProperties:
type: string
description: Custom HTTP headers to use when interacting with
this Grafana.
type: object
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
7 changes: 7 additions & 0 deletions docs/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3785,6 +3785,13 @@ Client defines how the grafana-operator talks to the grafana instance.
</tr>
</thead>
<tbody><tr>
<td><b>headers</b></td>
<td>map[string]string</td>
<td>
Custom HTTP headers to use when interacting with this Grafana.<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>preferIngress</b></td>
<td>boolean</td>
<td>
Expand Down

0 comments on commit 73ef281

Please sign in to comment.