Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions CLAUDE.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ The MVP is deliberately "provision, bootstrap, observe". It does:
- **serve OpenMetrics** from every instance on port 9091, declared on the pods and the headless Services and pinned to that format, so a Prometheus you already run scrapes the cluster with a ServiceMonitor, PodMonitor or scrape config of your own — and, on request, create the ServiceMonitor for a Prometheus Operator and the "Memgraph OpenMetrics" dashboard ConfigMap for a Grafana sidecar, or run the vmagent that pushes every instance's metrics to the VictoriaMetrics Memgraph runs and the Vector sidecars that push every instance's logs to its VictoriaLogs, so Memgraph can monitor the cluster without reaching into your network (any Prometheus remote-write and any Loki endpoint work), with `spec.monitoring` (see [`docs/monitoring.md`](docs/monitoring.md));
- **serve Bolt and the metrics endpoint over TLS** from a `kubernetes.io/tls` Secret you name in `spec.tls.bolt`, on both roles, with the operator's own dials and the ServiceMonitor following suit, and **authenticate the members to each other over mutual TLS** from a Secret with `ca.crt` you name in `spec.tls.intraCluster`, decided at creation (see [`docs/tls.md`](docs/tls.md));
- **keep the pods of a role on distinct nodes**, softly by default, as hard as `required` with scope `role` (the HA chart's `parity`) or `cluster` (its `unique`) on request, and pass a node selector, tolerations, topology spread constraints and a priority class through per role with `spec.scheduling` (see [`docs/scheduling.md`](docs/scheduling.md));
- report the observed MAIN, the registered member counts, the external addresses, and the readiness and convergence conditions on the resource's status.
- report the observed MAIN, the registered member counts, the external addresses, and the readiness and convergence conditions on the resource's status, and record an Event on it for every action it takes: each Bolt command Memgraph accepts or refuses, each setting it writes, each pod it restarts (see [`docs/events.md`](docs/events.md)).

Scaling is one edit, and `Converged` tells you when it is finished:

Expand Down
46 changes: 24 additions & 22 deletions api/v1alpha1/memgraphcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,20 @@ type SecretsSpec struct {
OrganizationKey string `json:"organizationKey,omitempty"`
}

// SecretReference names a Secret the cluster's pods consume. It is a struct
// rather than a bare name so that a field qualifying the reference, such as a
// namespace, can be added later without breaking a stored object. Today the
// Secret must live in the cluster's namespace: a pod mounts and reads into its
// environment only Secrets of its own namespace.
type SecretReference struct {
// name is the name of the Secret in the cluster's namespace.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
// +required
Name string `json:"name"`
}

// AWSCredentialsSpec references an existing Kubernetes Secret holding the AWS
// configuration of the data instances, all four values under fixed keys:
// AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION and AWS_ENDPOINT_URL.
Expand All @@ -551,15 +565,12 @@ type SecretsSpec struct {
// values until SET DATABASE SETTING clears them, for example
// SET DATABASE SETTING "aws.access_key" TO "".
type AWSCredentialsSpec struct {
// secretName names a Secret in the cluster's namespace holding the access
// secretRef names a Secret in the cluster's namespace holding the access
// key under AWS_ACCESS_KEY_ID, the secret key under AWS_SECRET_ACCESS_KEY,
// the region under AWS_REGION and the endpoint URL under
// AWS_ENDPOINT_URL.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
// +required
SecretName string `json:"secretName"`
SecretRef SecretReference `json:"secretRef"`
}

// RoleStorageSpec configures the PersistentVolumeClaim every pod of a role
Expand Down Expand Up @@ -1725,13 +1736,10 @@ type VMAgentImageSpec struct {
// reach it through environment variables, which Vector interpolates into its
// configuration.
type BasicAuthSecretSpec struct {
// secretName is the name of the Secret in the cluster's namespace holding
// the keys "username" and "password".
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
// secretRef names a Secret in the cluster's namespace holding the keys
// "username" and "password".
// +required
SecretName string `json:"secretName"`
SecretRef SecretReference `json:"secretRef"`
}

// RemoteWriteSpec is where vmagent ships what it scrapes.
Expand Down Expand Up @@ -1956,16 +1964,13 @@ type MonitoringSpec struct {
// every instance. Pointing at a differently named Secret is a pod-template
// change and rolls the cluster instead.
type BoltTLSSpec struct {
// secretName names a Secret in the cluster's namespace holding the
// secretRef names a Secret in the cluster's namespace holding the
// certificate under tls.crt and the private key under tls.key — the shape
// of a kubernetes.io/tls Secret, which kubectl create secret tls and a
// cert-manager Certificate both produce. Both roles mount it read-only and
// serve Bolt and metrics with it.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
// +required
SecretName string `json:"secretName"`
SecretRef SecretReference `json:"secretRef"`
}

// IntraClusterTLSSpec makes the members of the cluster talk to each other
Expand All @@ -1987,7 +1992,7 @@ type BoltTLSSpec struct {
// the still-plaintext MAIN can no longer replicate to it, and lag never
// converges. Adding or removing the block on a live cluster is therefore
// refused at admission (the rule lives on the spec, where both the old and
// the new tls block are in view). Changing secretName inside the block is
// the new tls block are in view). Pointing secretRef at another Secret is
// allowed and rolls the cluster; both Secrets must then chain to a CA the
// other side trusts, so a CA cut-over needs ca.crt in both to be a bundle of
// the old and the new CA.
Expand All @@ -1996,16 +2001,13 @@ type BoltTLSSpec struct {
// in-place update reaches every pod's files, Raft picks them up on its own
// and the rest on RELOAD INTRA_CLUSTER TLS, issued on every instance.
type IntraClusterTLSSpec struct {
// secretName names a Secret in the cluster's namespace holding the
// secretRef names a Secret in the cluster's namespace holding the
// certificate under tls.crt, the private key under tls.key and the CA to
// verify peers against under ca.crt — what a cert-manager Certificate
// issued by a private CA writes. Every pod of both roles mounts it
// read-only.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
// +required
SecretName string `json:"secretName"`
SecretRef SecretReference `json:"secretRef"`
}

// TLSSpec holds the cluster's TLS modes, each an optional presence-based block
Expand Down
19 changes: 19 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

2 changes: 1 addition & 1 deletion charts/memgraph-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Kubernetes operator for Memgraph high-availability clusters
type: application
# version is the chart version, appVersion the operator version it installs.
# Both move together with the git tag at release time.
version: 1.0.1-rc.3
version: 1.0.1-rc.4
appVersion: "1.0.1-rc.1"
# The CustomResourceDefinition uses CEL validation rules
# (x-kubernetes-validations), available from Kubernetes 1.25.
Expand Down
109 changes: 72 additions & 37 deletions charts/memgraph-operator/crds/memgraph.com_memgraphclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,25 @@ spec:
AWS integration (LOAD PARQUET and the other S3 readers). Absent, the
operator sets none of them.
properties:
secretName:
secretRef:
description: |-
secretName names a Secret in the cluster's namespace holding the access
secretRef names a Secret in the cluster's namespace holding the access
key under AWS_ACCESS_KEY_ID, the secret key under AWS_SECRET_ACCESS_KEY,
the region under AWS_REGION and the endpoint URL under
AWS_ENDPOINT_URL.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
properties:
name:
description: name is the name of the Secret in the cluster's
namespace.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
required:
- secretName
- secretRef
type: object
clusterDomain:
default: cluster.local
Expand Down Expand Up @@ -1051,16 +1058,23 @@ spec:
auth names the Secret Vector authenticates to the endpoint with. Absent,
the endpoint is pushed to unauthenticated.
properties:
secretName:
secretRef:
description: |-
secretName is the name of the Secret in the cluster's namespace holding
the keys "username" and "password".
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
secretRef names a Secret in the cluster's namespace holding the keys
"username" and "password".
properties:
name:
description: name is the name of the Secret in the
cluster's namespace.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
required:
- secretName
- secretRef
type: object
extraLabels:
additionalProperties:
Expand Down Expand Up @@ -1254,16 +1268,23 @@ spec:
basicAuth names the Secret vmagent authenticates to the endpoint with.
Absent, the endpoint is written to unauthenticated.
properties:
secretName:
secretRef:
description: |-
secretName is the name of the Secret in the cluster's namespace holding
the keys "username" and "password".
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
secretRef names a Secret in the cluster's namespace holding the keys
"username" and "password".
properties:
name:
description: name is the name of the Secret in
the cluster's namespace.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
required:
- secretName
- secretRef
type: object
url:
description: |-
Expand Down Expand Up @@ -3059,39 +3080,53 @@ spec:
bolt serves Bolt and the metrics endpoint over TLS on both roles, from
the certificate in the named Secret.
properties:
secretName:
secretRef:
description: |-
secretName names a Secret in the cluster's namespace holding the
secretRef names a Secret in the cluster's namespace holding the
certificate under tls.crt and the private key under tls.key — the shape
of a kubernetes.io/tls Secret, which kubectl create secret tls and a
cert-manager Certificate both produce. Both roles mount it read-only and
serve Bolt and metrics with it.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
properties:
name:
description: name is the name of the Secret in the cluster's
namespace.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
required:
- secretName
- secretRef
type: object
intraCluster:
description: |-
intraCluster makes the members talk to each other over mutual TLS from
the certificate and CA in the named Secret. It cannot be added to or
removed from a live cluster; see IntraClusterTLSSpec.
properties:
secretName:
secretRef:
description: |-
secretName names a Secret in the cluster's namespace holding the
secretRef names a Secret in the cluster's namespace holding the
certificate under tls.crt, the private key under tls.key and the CA to
verify peers against under ca.crt — what a cert-manager Certificate
issued by a private CA writes. Every pod of both roles mounts it
read-only.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
properties:
name:
description: name is the name of the Secret in the cluster's
namespace.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
required:
- secretName
- secretRef
type: object
type: object
userContainers:
Expand Down
7 changes: 7 additions & 0 deletions charts/memgraph-operator/rbac/manager-rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@
- list
- patch
- watch
- apiGroups:
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- apiGroups:
- gateway.networking.k8s.io
resources:
Expand Down
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ func main() {
Scheme: mgr.GetScheme(),
APIReader: mgr.GetAPIReader(),
Memgraph: memgraph.NewBoltConnector(),
Recorder: mgr.GetEventRecorder("memgraph-operator"),
GatewayAPI: gatewayAPI,
GatewayAPIMissing: gatewayAPIMissing,
ServiceMonitorAPI: serviceMonitorAPI,
Expand Down
Loading
Loading