Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(client): add support for custom HTTP headers #1756

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions api/v1beta1/grafana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ type GrafanaClient struct {
// TLS Configuration used to talk with the grafana instance.
// +optional
TLS *TLSConfig `json:"tls,omitempty"`
// Optional list of Header configuration objects that specify headers key and their value.
// +optional
Headers *[]Header `json:"headers,omitempty"`
xorilog marked this conversation as resolved.
Show resolved Hide resolved
}

// Header specifies http.Header key and value
type Header struct {
// Header name
Key string `json:"key,omitempty"`
// Header value
Value string `json:"value,omitempty"`
}

// GrafanaPreferences holds Grafana preferences API settings
Expand All @@ -138,8 +149,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 +165,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
24 changes: 24 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.

14 changes: 14 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,20 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
description: Optional list of Header configuration objects that
specify headers key and their value.
items:
description: Header specifies http.Header key and value
properties:
key:
description: Header name
type: string
value:
description: Header value
type: string
type: object
type: array
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
17 changes: 17 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.Headers != nil {
transport.(*instrumentedRoundTripper).addHeaders(extractClientHeaders(grafana))
}

client := &http.Client{
Transport: transport,
Expand All @@ -161,7 +164,21 @@ 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
}

func extractClientHeaders(grafana *v1beta1.Grafana) map[string]string {
if grafana.Spec.Client.Headers == nil {
return nil
}

headers := make(map[string]string, len(*grafana.Spec.Client.Headers))
for _, h := range *grafana.Spec.Client.Headers {
headers[h.Key] = h.Value
}

return headers
}
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.Headers != nil {
transport.(*instrumentedRoundTripper).addHeaders(extractClientHeaders(grafana))
}

return &http.Client{
Transport: NewInstrumentedRoundTripper(grafana.Name, metrics.GrafanaApiRequests, grafana.IsExternal(), tlsConfig),
Transport: transport,
Timeout: time.Second * timeout,
}, nil
}
20 changes: 19 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 @@ -33,11 +34,17 @@ func NewInstrumentedRoundTripper(relatedResource string, metric *prometheus.Coun
relatedResource: relatedResource,
wrapped: transport,
metric: metric,
headers: map[string]string{"user-agent": "grafana-operator/" + embeds.Version},
xorilog marked this conversation as resolved.
Show resolved Hide resolved
}
}

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 +55,14 @@ func (in *instrumentedRoundTripper) RoundTrip(r *http.Request) (*http.Response,
}
return resp, err
}

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

for k, v := range headers {
in.headers[k] = v
}
}
xorilog marked this conversation as resolved.
Show resolved Hide resolved
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,20 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
description: Optional list of Header configuration objects that
specify headers key and their value.
items:
description: Header specifies http.Header key and value
properties:
key:
description: Header name
type: string
value:
description: Header value
type: string
type: object
type: array
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
14 changes: 14 additions & 0 deletions deploy/kustomize/base/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,20 @@ spec:
description: Client defines how the grafana-operator talks to the
grafana instance.
properties:
headers:
description: Optional list of Header configuration objects that
specify headers key and their value.
items:
description: Header specifies http.Header key and value
properties:
key:
description: Header name
type: string
value:
description: Header value
type: string
type: object
type: array
preferIngress:
description: If the operator should send it's request through
the grafana instances ingress object instead of through the
Expand Down
41 changes: 41 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><a href="#grafanaspecclientheadersindex">headers</a></b></td>
<td>[]object</td>
<td>
Optional list of Header configuration objects that specify headers key and their value.<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>preferIngress</b></td>
<td>boolean</td>
<td>
Expand All @@ -3811,6 +3818,40 @@ Client defines how the grafana-operator talks to the grafana instance.
</table>


### Grafana.spec.client.headers[index]
<sup><sup>[↩ Parent](#grafanaspecclient)</sup></sup>



Header specifies http.Header key and value

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><b>key</b></td>
<td>string</td>
<td>
Header name<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>value</b></td>
<td>string</td>
<td>
Header value<br/>
</td>
<td>false</td>
</tr></tbody>
</table>


### Grafana.spec.client.tls
<sup><sup>[↩ Parent](#grafanaspecclient)</sup></sup>

Expand Down