Skip to content

Commit b4932a2

Browse files
Merge pull request #44 from uselagoon/calculate-databases
Improve database storage calculation
2 parents 79f4b52 + e9a7060 commit b4932a2

File tree

10 files changed

+688
-374
lines changed

10 files changed

+688
-374
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ Storage-calculator connects to rabbitmq in lagoon-core and publishes to the acti
2424
"claims": [{
2525
"environment": 1,
2626
"persistentStorageClaim": "nginx",
27-
"bytesUsed": 1200
27+
"kibUsed": 1200
2828
}, {
2929
"environment": 1,
3030
"persistentStorageClaim": "solr",
31-
"bytesUsed": 2200
31+
"kibUsed": 2200
3232
}, {
3333
"environment": 1,
3434
"persistentStorageClaim": "mariadb",
35-
"bytesUsed": 3200
35+
"kibUsed": 3200
3636
}]
3737
}
3838
}

cmd/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import (
3535
_ "k8s.io/client-go/plugin/pkg/client/auth"
3636

3737
// "github.com/cheshir/go-mq"
38+
mariadbv1 "github.com/amazeeio/dbaas-operator/apis/mariadb/v1"
39+
postgresv1 "github.com/amazeeio/dbaas-operator/apis/postgres/v1"
3840
mq "github.com/cheshir/go-mq/v2"
3941
"k8s.io/apimachinery/pkg/runtime"
4042
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -62,6 +64,8 @@ var (
6264

6365
func init() {
6466
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
67+
utilruntime.Must(mariadbv1.AddToScheme(scheme))
68+
utilruntime.Must(postgresv1.AddToScheme(scheme))
6569

6670
//+kubebuilder:scaffold:scheme
6771
}
@@ -122,7 +126,7 @@ func main() {
122126
"The retry interval for rabbitmq.")
123127
flag.StringVar(&lagoonAppID, "lagoon-app-id", "storage-calculator",
124128
"The appID to use that will be sent with messages.")
125-
flag.StringVar(&storageCalculatorImage, "storage-calculator-image", "imagecache.amazeeio.cloud/amazeeio/alpine-mysql-client",
129+
flag.StringVar(&storageCalculatorImage, "storage-calculator-image", "uselagoon/database-tools",
126130
"The image to use for storage-calculator pods.")
127131
flag.BoolVar(&exportPrometheusMetrics, "prometheus-metrics", false, "Export prometheus metrics.")
128132

config/rbac/role.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ rules:
4848
verbs:
4949
- get
5050
- list
51+
- apiGroups:
52+
- mariadb.amazee.io
53+
- postgres.amazee.io
54+
resources:
55+
- mariadbconsumers
56+
- postgresqlconsumers
57+
verbs:
58+
- list
59+
- watch

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/uselagoon/storage-calculator
33
go 1.24.0
44

55
require (
6+
github.com/amazeeio/dbaas-operator v0.4.0
67
github.com/cheshir/go-mq/v2 v2.0.1
78
github.com/go-logr/logr v1.4.3
89
github.com/onsi/ginkgo/v2 v2.25.2

go.sum

Lines changed: 85 additions & 0 deletions
Large diffs are not rendered by default.

internal/storage/databases.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package storage
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"regexp"
8+
"strconv"
9+
"strings"
10+
11+
mariadbv1 "github.com/amazeeio/dbaas-operator/apis/mariadb/v1"
12+
postgresv1 "github.com/amazeeio/dbaas-operator/apis/postgres/v1"
13+
"github.com/go-logr/logr"
14+
corev1 "k8s.io/api/core/v1"
15+
"k8s.io/apimachinery/pkg/api/meta"
16+
client "sigs.k8s.io/controller-runtime/pkg/client"
17+
)
18+
19+
// +kubebuilder:rbac:groups=mariadb.amazee.io;postgres.amazee.io,resources=mariadbconsumers;postgresqlconsumers,verbs=list;watch
20+
21+
// checkDatabasesCreatePods calculates storage usage for databases managed by dbaas-operator.
22+
// Databases running as a pod are handled by the volumes calculator.
23+
func (c *Calculator) checkDatabasesCreatePods(
24+
ctx context.Context,
25+
opLog logr.Logger,
26+
namespace corev1.Namespace,
27+
) (int, error) {
28+
opLog = opLog.WithName("Databases")
29+
ignoreRegex := c.IgnoreRegex
30+
if value, ok := namespace.Labels["lagoon.sh/storageCalculatorIgnoreRegex"]; ok {
31+
ignoreRegex = value
32+
}
33+
34+
environmentID := 0
35+
if value, ok := namespace.Labels["lagoon.sh/environmentId"]; ok {
36+
environmentID, _ = strconv.Atoi(strings.TrimSpace(value))
37+
}
38+
39+
nsListOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
40+
client.InNamespace(namespace.Name),
41+
})
42+
43+
// Check for managed MariaDB databases.
44+
mariadbs := []mariadbv1.MariaDBConsumer{}
45+
mariadbList := &mariadbv1.MariaDBConsumerList{}
46+
if err := c.Client.List(ctx, mariadbList, nsListOption); err != nil {
47+
if !meta.IsNoMatchError(err) {
48+
opLog.Error(err, fmt.Sprintf("error getting mariadbconsumer for namespace %s", namespace.Name))
49+
}
50+
} else {
51+
for _, mariadb := range mariadbList.Items {
52+
if ignoreRegex != "" {
53+
match, _ := regexp.MatchString(ignoreRegex, mariadb.Name)
54+
if match {
55+
opLog.Info(fmt.Sprintf("ignoring mariadbconsumer %s", mariadb.Name))
56+
continue
57+
}
58+
}
59+
mariadbs = append(mariadbs, mariadb)
60+
}
61+
}
62+
63+
// Check for managed PostgreSQL databases.
64+
postgresdbs := []postgresv1.PostgreSQLConsumer{}
65+
postgresList := &postgresv1.PostgreSQLConsumerList{}
66+
if err := c.Client.List(ctx, postgresList, nsListOption); err != nil {
67+
if !meta.IsNoMatchError(err) {
68+
opLog.Error(err, fmt.Sprintf("error getting postgresqlconsumer for namespace %s", namespace.Name))
69+
}
70+
} else {
71+
for _, postgres := range postgresList.Items {
72+
if ignoreRegex != "" {
73+
match, _ := regexp.MatchString(ignoreRegex, postgres.Name)
74+
if match {
75+
opLog.Info(fmt.Sprintf("ignoring postgresconsumer %s", postgres.Name))
76+
continue
77+
}
78+
}
79+
postgresdbs = append(postgresdbs, postgres)
80+
}
81+
}
82+
83+
if len(mariadbs) == 0 && len(postgresdbs) == 0 {
84+
opLog.Info(fmt.Sprintf("no databases in %s", namespace.Name))
85+
return 0, nil
86+
}
87+
88+
// Calculate storage for all databases.
89+
services := databasesCalculatorPod{
90+
MariaDB: mariadbs,
91+
PostgreSQL: postgresdbs,
92+
}
93+
storageClaims, err := c.createDatabasePod(ctx, opLog, namespace, services, environmentID)
94+
if err != nil {
95+
return 0, fmt.Errorf("error calculating databases storage: %w", err)
96+
}
97+
98+
if len(storageClaims) == 0 {
99+
return 0, nil
100+
}
101+
102+
// Report storage sizes for all databases.
103+
actionData := ActionEvent{
104+
Type: "updateEnvironmentStorage",
105+
EventType: "environmentStorage",
106+
Data: ActionData{
107+
Claims: storageClaims,
108+
},
109+
Meta: MetaData{
110+
Namespace: namespace.Name,
111+
Project: namespace.Labels["lagoon.sh/project"],
112+
Environment: namespace.Labels["lagoon.sh/environment"],
113+
},
114+
}
115+
opLog.Info(fmt.Sprintf("storage in %s: %v", namespace.Name, actionData))
116+
117+
if c.ExportMetrics {
118+
actionData.ExportMetrics(c.PromStorage)
119+
}
120+
121+
actionDataJSON, _ := json.Marshal(actionData)
122+
if err := c.MQ.Publish("lagoon-actions", actionDataJSON); err != nil {
123+
return 0, fmt.Errorf("error publishing databases storage to mq: %v", err)
124+
}
125+
126+
return len(storageClaims), nil
127+
}

internal/storage/main.go

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package storage
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"strconv"
8+
"strings"
79

810
"github.com/go-logr/logr"
911
"github.com/prometheus/client_golang/prometheus"
@@ -71,9 +73,7 @@ type ActionData struct {
7173
type StorageClaim struct {
7274
Environment int `json:"environment"`
7375
PersisteStorageClaim string `json:"persistentStorageClaim"`
74-
// it is actually kilobytes that du outputs, this should be deprecated
75-
BytesUsed uint64 `json:"bytesUsed"`
76-
KiBUsed uint64 `json:"kibUsed"`
76+
KiBUsed uint64 `json:"kibUsed"`
7777
}
7878

7979
// Calculate will run the storage-calculator job.
@@ -100,9 +100,70 @@ func (c *Calculator) Calculate() {
100100
return
101101
}
102102
for _, namespace := range namespaces.Items {
103-
err := c.checkVolumesCreatePods(ctx, opLog, namespace)
104-
if err != nil {
105-
continue
103+
opLog.Info(fmt.Sprintf("calculating storage for namespace %s", namespace.Name))
104+
105+
// Cleanup old calculator pods if they are still running.
106+
p1, _ := labels.NewRequirement("lagoon.sh/storageCalculator", "in", []string{"true"})
107+
labelRequirements := []labels.Requirement{}
108+
labelRequirements = append(labelRequirements, *p1)
109+
podListOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
110+
client.InNamespace(namespace.Name),
111+
client.MatchingLabelsSelector{
112+
Selector: labels.NewSelector().Add(labelRequirements...),
113+
},
114+
})
115+
pods := &corev1.PodList{}
116+
if err := c.Client.List(ctx, pods, podListOption); err != nil {
117+
opLog.Error(err, fmt.Sprintf("error getting storage-calculator pods for namespace %s", namespace.Name))
118+
}
119+
for _, pod := range pods.Items {
120+
c.cleanup(ctx, opLog, &pod)
121+
}
122+
123+
// Calculate storage for volumes and databases.
124+
volClaims, volErr := c.checkVolumesCreatePods(ctx, opLog, namespace)
125+
if volErr != nil {
126+
opLog.Error(volErr, "error calculating volumes")
127+
}
128+
dbClaims, dbErr := c.checkDatabasesCreatePods(ctx, opLog, namespace)
129+
if dbErr != nil {
130+
opLog.Error(dbErr, "error calculating databases")
131+
}
132+
133+
// Report a "none" claim to differentiate between no storage used and no
134+
// storage recorded (due to error or crash)
135+
if (volClaims == 0 && volErr != nil) && (dbClaims == 0 && dbErr != nil) {
136+
environmentID := 0
137+
if value, ok := namespace.Labels["lagoon.sh/environmentId"]; ok {
138+
environmentID, _ = strconv.Atoi(strings.TrimSpace(value))
139+
}
140+
141+
storData := ActionData{}
142+
storData.Claims = append(storData.Claims, StorageClaim{
143+
Environment: environmentID,
144+
PersisteStorageClaim: "none",
145+
KiBUsed: 0,
146+
})
147+
actionData := ActionEvent{
148+
Type: "updateEnvironmentStorage",
149+
EventType: "environmentStorage",
150+
Data: storData,
151+
Meta: MetaData{
152+
Namespace: namespace.Name,
153+
Project: namespace.Labels["lagoon.sh/project"],
154+
Environment: namespace.Labels["lagoon.sh/environment"],
155+
},
156+
}
157+
opLog.Info(fmt.Sprintf("no storage used in %s: %v", namespace.Name, actionData))
158+
159+
if c.ExportMetrics {
160+
actionData.ExportMetrics(c.PromStorage)
161+
}
162+
163+
actionDataJson, _ := json.Marshal(actionData)
164+
if err := c.MQ.Publish("lagoon-actions", actionDataJson); err != nil {
165+
opLog.Error(err, "error publishing message to mq")
166+
}
106167
}
107168
}
108169
}

0 commit comments

Comments
 (0)