Skip to content

Commit

Permalink
feat: enable HTTPS in OpenShift clusters
Browse files Browse the repository at this point in the history
This change adds an end-to-end test ensuring that the operator's
/metrics endpoint works as expected.

Signed-off-by: Simon Pasquier <[email protected]>
  • Loading branch information
simonpasquier committed Oct 21, 2024
1 parent b1c2cc7 commit fe1f665
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ metadata:
categories: Monitoring
certified: "false"
containerImage: observability-operator:0.4.2
createdAt: "2024-10-08T12:44:05Z"
createdAt: "2024-10-17T13:23:55Z"
description: A Go based Kubernetes operator to setup and manage highly available
Monitoring Stack using Prometheus, Alertmanager and Thanos Querier.
operators.operatorframework.io/builder: operator-sdk-v1.36.1
Expand Down Expand Up @@ -745,6 +745,10 @@ spec:
capabilities:
drop:
- ALL
volumeMounts:
- mountPath: /etc/tls/private
name: observability-operator-tls
readOnly: true
securityContext:
runAsNonRoot: true
serviceAccountName: observability-operator-sa
Expand All @@ -753,6 +757,11 @@ spec:
- effect: NoSchedule
key: node-role.kubernetes.io/infra
operator: Exists
volumes:
- name: observability-operator-tls
secret:
optional: true
secretName: observability-operator-tls
strategy: deployment
installModes:
- supported: false
Expand Down
2 changes: 2 additions & 0 deletions bundle/manifests/observability-operator_v1_service.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.openshift.io/serving-cert-secret-name: observability-operator-tls
creationTimestamp: null
labels:
app.kubernetes.io/component: operator
Expand Down
4 changes: 3 additions & 1 deletion cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ func main() {
os.Exit(1)
}

ctx := ctrl.SetupSignalHandler()

op, err := operator.New(
ctx,
operator.NewOperatorConfiguration(
operator.WithMetricsAddr(metricsAddr),
operator.WithHealthProbeAddr(healthProbeAddr),
Expand All @@ -134,7 +137,6 @@ func main() {
os.Exit(1)
}

ctx := ctrl.SetupSignalHandler()
setupLog.Info("starting manager")
if err := op.Start(ctx); err != nil {
setupLog.Error(err, "terminating")
Expand Down
9 changes: 9 additions & 0 deletions deploy/operator/observability-operator-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,14 @@ spec:
httpGet:
path: /healthz
port: 8081
volumeMounts:
- mountPath: /etc/tls/private
name: observability-operator-tls
readOnly: true
serviceAccountName: observability-operator-sa
volumes:
- name: observability-operator-tls
secret:
secretName: observability-operator-tls
optional: true
terminationGracePeriodSeconds: 30
2 changes: 2 additions & 0 deletions deploy/operator/observability-operator-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ metadata:
app.kubernetes.io/component: operator
app.kubernetes.io/name: observability-operator
app.kubernetes.io/part-of: observability-operator
annotations:
service.beta.openshift.io/serving-cert-secret-name: observability-operator-tls
spec:
selector:
app.kubernetes.io/name: observability-operator
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
k8s.io/api v0.31.1
k8s.io/apiextensions-apiserver v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/apiserver v0.31.1
k8s.io/client-go v0.31.1
k8s.io/component-base v0.31.1
k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/
k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c=
k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
Expand Down
122 changes: 107 additions & 15 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ package operator

import (
"context"
"crypto/tls"
"fmt"

"os"
"path/filepath"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
"k8s.io/client-go/kubernetes"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
Expand All @@ -15,15 +23,22 @@ import (
uictrl "github.com/rhobs/observability-operator/pkg/controllers/uiplugin"
)

// NOTE: The instance selector label is hardcoded in static assets.
// Any change to that must be reflected here as well
const instanceSelector = "app.kubernetes.io/managed-by=observability-operator"
const (
// NOTE: The instance selector label is hardcoded in static assets.
// Any change to that must be reflected here as well
instanceSelector = "app.kubernetes.io/managed-by=observability-operator"

ObservabilityOperatorName = "observability-operator"

const ObservabilityOperatorName = "observability-operator"
// The mount path for the serving certificate seret is hardcoded in the
// static assets.
tlsMountPath = "/etc/tls/private"
)

// Operator embedds manager and exposes only the minimal set of functions
type Operator struct {
manager manager.Manager
manager manager.Manager
servingCertController *dynamiccertificates.DynamicServingCertificateController
}

type OpenShiftFeatureGates struct {
Expand Down Expand Up @@ -102,14 +117,86 @@ func NewOperatorConfiguration(opts ...func(*OperatorConfiguration)) *OperatorCon
return cfg
}

func New(cfg *OperatorConfiguration) (*Operator, error) {
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: NewScheme(cfg),
Metrics: metricsserver.Options{
BindAddress: cfg.MetricsAddr,
},
HealthProbeBindAddress: cfg.HealthProbeAddr,
})
func New(ctx context.Context, cfg *OperatorConfiguration) (*Operator, error) {
restConfig := ctrl.GetConfigOrDie()

metricsOpts := metricsserver.Options{
BindAddress: cfg.MetricsAddr,
}

var servingCertController *dynamiccertificates.DynamicServingCertificateController
if cfg.FeatureGates.OpenShift.Enabled {
certFile := filepath.Join(tlsMountPath, "tls.crt")
keyFile := filepath.Join(tlsMountPath, "tls.key")

// Wait for files to be present.
var pollErr error
err := wait.PollUntilContextTimeout(ctx, time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
for _, f := range []string{certFile, keyFile} {
if _, err := os.Stat(f); err != nil {
pollErr = err
return false, nil
}
}

return true, nil
})
if err != nil {
return nil, fmt.Errorf("%w: %w", err, pollErr)
}

certKeyProvider, err := dynamiccertificates.NewDynamicServingContentFromFiles("serving-cert", certFile, keyFile)
if err != nil {
return nil, err
}
if err := certKeyProvider.RunOnce(ctx); err != nil {
return nil, fmt.Errorf("failed to initialize cert/key content: %w", err)
}

kubeClient, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return nil, err
}

clientCAProvider, err := dynamiccertificates.NewDynamicCAFromConfigMapController(
"client-ca",
metav1.NamespaceSystem,
"extension-apiserver-authentication",
"client-ca-file",
kubeClient,
)
if err != nil {
return nil, fmt.Errorf("failed to initialize client CA controller: %w", err)
}

servingCertController = dynamiccertificates.NewDynamicServingCertificateController(
&tls.Config{
ClientAuth: tls.NoClientCert,
},
clientCAProvider,
certKeyProvider,
nil,
nil,
)
if err := servingCertController.RunOnce(); err != nil {
return nil, fmt.Errorf("failed to initialize serving certificate controller: %w", err)
}

metricsOpts.SecureServing = true
metricsOpts.TLSOpts = []func(*tls.Config){
func(c *tls.Config) {
c.GetConfigForClient = servingCertController.GetConfigForClient
},
}
}

mgr, err := ctrl.NewManager(
restConfig,
ctrl.Options{
Scheme: NewScheme(cfg),
Metrics: metricsOpts,
HealthProbeBindAddress: cfg.HealthProbeAddr,
})
if err != nil {
return nil, fmt.Errorf("unable to create manager: %w", err)
}
Expand Down Expand Up @@ -141,11 +228,16 @@ func New(cfg *OperatorConfiguration) (*Operator, error) {
}

return &Operator{
manager: mgr,
manager: mgr,
servingCertController: servingCertController,
}, nil
}

func (o *Operator) Start(ctx context.Context) error {
if o.servingCertController != nil {
go o.servingCertController.Run(1, ctx.Done())
}

if err := o.manager.Start(ctx); err != nil {
return fmt.Errorf("unable to start manager: %w", err)
}
Expand Down
Loading

0 comments on commit fe1f665

Please sign in to comment.