mirror of
https://github.com/clastix/kamaji.git
synced 2026-02-14 10:00:02 +00:00
feat: postgresql kine driver
This commit is contained in:
@@ -81,7 +81,7 @@ func getDefaultDeleteableResources(config GroupDeleteableResourceBuilderConfigur
|
||||
Endpoints: getArrayFromString(config.tcpReconcilerConfig.ETCDEndpoints),
|
||||
},
|
||||
}
|
||||
case types.KineMySQL:
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
return []resources.DeleteableResource{
|
||||
&resources.SQLSetup{
|
||||
Client: config.client,
|
||||
@@ -215,13 +215,14 @@ func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcil
|
||||
Endpoints: getArrayFromString(tcpReconcilerConfig.ETCDEndpoints),
|
||||
},
|
||||
}
|
||||
case types.KineMySQL:
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
return []resources.Resource{
|
||||
&resources.SQLStorageConfig{
|
||||
Client: c,
|
||||
Name: "sql-config",
|
||||
Host: dbConnection.GetHost(),
|
||||
Port: dbConnection.GetPort(),
|
||||
Driver: dbConnection.Driver(),
|
||||
},
|
||||
&resources.SQLSetup{
|
||||
Client: c,
|
||||
@@ -232,8 +233,8 @@ func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcil
|
||||
Client: c,
|
||||
Name: "sql-certificate",
|
||||
StorageType: tcpReconcilerConfig.ETCDStorageType,
|
||||
SQLConfigSecretName: tcpReconcilerConfig.KineMySQLSecretName,
|
||||
SQLConfigSecretNamespace: tcpReconcilerConfig.KineMySQLSecretNamespace,
|
||||
SQLConfigSecretName: tcpReconcilerConfig.KineSecretName,
|
||||
SQLConfigSecretNamespace: tcpReconcilerConfig.KineSecretNamespace,
|
||||
},
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
@@ -17,41 +19,67 @@ import (
|
||||
)
|
||||
|
||||
func (r *TenantControlPlaneReconciler) getStorageConnection(ctx context.Context) (sql.DBConnection, error) {
|
||||
var driver sql.Driver
|
||||
var dbName string
|
||||
|
||||
// TODO: https://github.com/clastix/kamaji/issues/67
|
||||
switch r.Config.ETCDStorageType {
|
||||
case types.ETCD:
|
||||
return nil, nil
|
||||
case types.KineMySQL:
|
||||
secret := &corev1.Secret{}
|
||||
namespacedName := k8stypes.NamespacedName{Namespace: r.Config.KineMySQLSecretNamespace, Name: r.Config.KineMySQLSecretName}
|
||||
if err := r.Client.Get(ctx, namespacedName, secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootCAs := x509.NewCertPool()
|
||||
if ok := rootCAs.AppendCertsFromPEM(secret.Data["ca.crt"]); !ok {
|
||||
return nil, fmt.Errorf("error creating root ca for mysql db connector")
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(secret.Data["server.crt"], secret.Data["server.key"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sql.GetDBConnection(
|
||||
sql.ConnectionConfig{
|
||||
SQLDriver: sql.MySQL,
|
||||
User: "root",
|
||||
Password: string(secret.Data["MYSQL_ROOT_PASSWORD"]),
|
||||
Host: r.Config.KineMySQLHost,
|
||||
Port: r.Config.KineMySQLPort,
|
||||
DBName: "mysql",
|
||||
TLSConfig: &tls.Config{
|
||||
ServerName: r.Config.KineMySQLHost,
|
||||
RootCAs: rootCAs,
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
},
|
||||
},
|
||||
)
|
||||
driver = sql.MySQL
|
||||
dbName = "mysql"
|
||||
case types.KinePostgreSQL:
|
||||
driver = sql.PostgreSQL
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
namespacedName := k8stypes.NamespacedName{Namespace: r.Config.KineSecretNamespace, Name: r.Config.KineSecretName}
|
||||
if err := r.Client.Get(ctx, namespacedName, secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t := "kamaji.clastix.io/kine"; string(secret.Type) != t {
|
||||
return nil, fmt.Errorf("expecting a secret of type %s", t)
|
||||
}
|
||||
|
||||
keys := []string{"ca.crt", "server.crt", "server.key", "username", "password"}
|
||||
|
||||
if secret.Data == nil {
|
||||
return nil, fmt.Errorf("the Kine secret %s/%s is missing all the required keys (%s)", secret.GetNamespace(), secret.GetName(), strings.Join(keys, ","))
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if _, ok := secret.Data[key]; !ok {
|
||||
return nil, fmt.Errorf("missing required key %s for the Kine secret %s/%s", key, secret.GetNamespace(), secret.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
rootCAs := x509.NewCertPool()
|
||||
if ok := rootCAs.AppendCertsFromPEM(secret.Data["ca.crt"]); !ok {
|
||||
return nil, fmt.Errorf("error create root CA for the DB connector")
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(secret.Data["server.crt"], secret.Data["server.key"])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot retrieve x.509 key pair from the Kine Secret")
|
||||
}
|
||||
|
||||
return sql.GetDBConnection(
|
||||
sql.ConnectionConfig{
|
||||
SQLDriver: driver,
|
||||
User: string(secret.Data["username"]),
|
||||
Password: string(secret.Data["password"]),
|
||||
Host: r.Config.KineHost,
|
||||
Port: r.Config.KinePort,
|
||||
DBName: dbName,
|
||||
TLSConfig: &tls.Config{
|
||||
ServerName: r.Config.KineHost,
|
||||
RootCAs: rootCAs,
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ type TenantControlPlaneReconcilerConfig struct {
|
||||
ETCDCompactionInterval string
|
||||
TmpBaseDirectory string
|
||||
DBConnection sql.DBConnection
|
||||
KineMySQLSecretName string
|
||||
KineMySQLSecretNamespace string
|
||||
KineMySQLHost string
|
||||
KineMySQLPort int
|
||||
KineSecretName string
|
||||
KineSecretNamespace string
|
||||
KineHost string
|
||||
KinePort int
|
||||
KineContainerImage string
|
||||
}
|
||||
|
||||
|
||||
11
go.mod
11
go.mod
@@ -4,8 +4,10 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.2.0
|
||||
github.com/go-pg/pg/v10 v10.10.6
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.17.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -70,6 +72,7 @@ require (
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
@@ -84,8 +87,8 @@ require (
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/lithammer/dedent v1.1.0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
@@ -117,6 +120,11 @@ require (
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/vmihailenco/bufpool v0.1.11 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
@@ -148,6 +156,7 @@ require (
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
|
||||
k8s.io/system-validators v1.6.0 // indirect
|
||||
mellium.im/sasl v0.3.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.10.1 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect
|
||||
|
||||
23
go.sum
23
go.sum
@@ -413,6 +413,10 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-pg/pg/v10 v10.10.6 h1:1vNtPZ4Z9dWUw/TjJwOfFUbF5nEq1IkR6yG8Mq/Iwso=
|
||||
github.com/go-pg/pg/v10 v10.10.6/go.mod h1:GLmFXufrElQHf5uzM3BQlcfwV3nsgnHue5uzjQ6Nqxg=
|
||||
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
@@ -591,6 +595,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||
github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
@@ -723,6 +729,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
@@ -909,6 +916,8 @@ github.com/testcontainers/testcontainers-go v0.13.0 h1:OUujSlEGsXVo/ykPVZk3KanBN
|
||||
github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBTzV6ntZAC1eZBYPtaFsn4nPuDk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
@@ -920,6 +929,14 @@ github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:tw
|
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
@@ -995,6 +1012,7 @@ go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -1010,6 +1028,7 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1249,6 +1268,7 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1626,6 +1646,9 @@ k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
|
||||
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ=
|
||||
mellium.im/sasl v0.3.0 h1:0qoaTCTo5Py7u/g0cBIQZcMOgG/5LM71nshbXwznBh8=
|
||||
mellium.im/sasl v0.3.0/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
|
||||
@@ -40,11 +40,13 @@ const (
|
||||
schedulerKubeconfig
|
||||
controllerManagerKubeconfig
|
||||
kineConfig
|
||||
kineCerts
|
||||
)
|
||||
|
||||
const (
|
||||
apiServerFlagsAnnotation = "kube-apiserver.kamaji.clastix.io/args"
|
||||
kineVolumeName = "kine-config"
|
||||
kineVolumeCertName = "kine-certs"
|
||||
)
|
||||
|
||||
type Deployment struct {
|
||||
@@ -592,18 +594,37 @@ func (d *Deployment) buildKineVolume(podSpec *corev1.PodSpec, tcp *kamajiv1alpha
|
||||
podSpec.Volumes = volumes
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.KineMySQL {
|
||||
if found, index := utilities.HasNamedVolume(podSpec.Volumes, kineVolumeCertName); found {
|
||||
var volumes []corev1.Volume
|
||||
|
||||
volumes = append(volumes, podSpec.Volumes[:index]...)
|
||||
volumes = append(volumes, podSpec.Volumes[index+1:]...)
|
||||
|
||||
podSpec.Volumes = volumes
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.KineMySQL || d.ETCDStorageType == types.KinePostgreSQL {
|
||||
if index := int(kineConfig) + 1; len(podSpec.Volumes) < index {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
|
||||
// Adding the volume to read Kine certificates:
|
||||
// these must be subsequently fixed with a chmod due to pg issues with private key.
|
||||
podSpec.Volumes[kineConfig].Name = kineVolumeName
|
||||
podSpec.Volumes[kineConfig].VolumeSource = corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: tcp.Status.Storage.KineMySQL.Certificate.SecretName,
|
||||
SecretName: tcp.Status.Storage.Kine.Certificate.SecretName,
|
||||
DefaultMode: pointer.Int32Ptr(420),
|
||||
},
|
||||
}
|
||||
// Adding the Volume for the certificates with fixed permission
|
||||
if index := int(kineCerts) + 1; len(podSpec.Volumes) < index {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
|
||||
podSpec.Volumes[kineCerts].Name = kineVolumeCertName
|
||||
podSpec.Volumes[kineCerts].VolumeSource = corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,60 +640,95 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
|
||||
podSpec.Containers = containers
|
||||
}
|
||||
// In case of bare ETCD we exit without mangling the PodSpec resource.
|
||||
if d.ETCDStorageType == types.ETCD {
|
||||
return
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.KineMySQL {
|
||||
if index := int(kineIndex) + 1; len(podSpec.Containers) < index {
|
||||
podSpec.Containers = append(podSpec.Containers, corev1.Container{})
|
||||
}
|
||||
if index := int(kineIndex) + 1; len(podSpec.Containers) < index {
|
||||
podSpec.Containers = append(podSpec.Containers, corev1.Container{})
|
||||
}
|
||||
|
||||
args := map[string]string{}
|
||||
args := map[string]string{}
|
||||
|
||||
if tcp.Spec.ControlPlane.Deployment.ExtraArgs != nil {
|
||||
args = utilities.ArgsFromSliceToMap(tcp.Spec.ControlPlane.Deployment.ExtraArgs.Kine)
|
||||
}
|
||||
if tcp.Spec.ControlPlane.Deployment.ExtraArgs != nil {
|
||||
args = utilities.ArgsFromSliceToMap(tcp.Spec.ControlPlane.Deployment.ExtraArgs.Kine)
|
||||
}
|
||||
|
||||
args["--endpoint"] = "mysql://$(MYSQL_USER):$(MYSQL_PASSWORD)@tcp($(MYSQL_HOST):$(MYSQL_PORT))/$(MYSQL_SCHEMA)"
|
||||
args["--ca-file"] = "/kine/ca.crt"
|
||||
args["--cert-file"] = "/kine/server.crt"
|
||||
args["--key-file"] = "/kine/server.key"
|
||||
switch d.ETCDStorageType {
|
||||
case types.KineMySQL:
|
||||
args["--endpoint"] = "mysql://$(DB_USER):$(DB_PASSWORD)@tcp($(DB_HOST):$(DB_PORT))/$(DB_SCHEMA)"
|
||||
case types.KinePostgreSQL:
|
||||
args["--endpoint"] = "postgres://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_SCHEMA)"
|
||||
}
|
||||
|
||||
podSpec.Containers[kineIndex].Name = kineContainerName
|
||||
podSpec.Containers[kineIndex].Image = d.KineContainerImage
|
||||
podSpec.Containers[kineIndex].Command = []string{"/bin/kine"}
|
||||
podSpec.Containers[kineIndex].Args = utilities.ArgsFromMapToSlice(args)
|
||||
podSpec.Containers[kineIndex].VolumeMounts = []corev1.VolumeMount{
|
||||
{
|
||||
Name: kineVolumeName,
|
||||
MountPath: "/kine",
|
||||
ReadOnly: true,
|
||||
args["--ca-file"] = "/certs/ca.crt"
|
||||
args["--cert-file"] = "/certs/server.crt"
|
||||
args["--key-file"] = "/certs/server.key"
|
||||
|
||||
podSpec.InitContainers = []corev1.Container{
|
||||
{
|
||||
Name: "chmod",
|
||||
Image: d.KineContainerImage,
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
TerminationMessagePath: corev1.TerminationMessagePathDefault,
|
||||
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
||||
Command: []string{"sh"},
|
||||
Args: []string{
|
||||
"-c",
|
||||
"cp /kine/*.* /certs && chmod -R 600 /certs/*.*",
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].TerminationMessagePath = corev1.TerminationMessagePathDefault
|
||||
podSpec.Containers[kineIndex].TerminationMessagePolicy = corev1.TerminationMessageReadFile
|
||||
podSpec.Containers[kineIndex].Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "GODEBUG",
|
||||
Value: "x509ignoreCN=0",
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].EnvFrom = []corev1.EnvFromSource{
|
||||
{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: tcp.Status.Storage.KineMySQL.Config.SecretName,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: kineVolumeName,
|
||||
ReadOnly: true,
|
||||
MountPath: "/kine",
|
||||
},
|
||||
{
|
||||
Name: kineVolumeCertName,
|
||||
MountPath: "/certs",
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].Ports = []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: 2379,
|
||||
Name: "server",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].ImagePullPolicy = corev1.PullAlways
|
||||
},
|
||||
}
|
||||
|
||||
podSpec.Containers[kineIndex].Name = kineContainerName
|
||||
podSpec.Containers[kineIndex].Image = d.KineContainerImage
|
||||
podSpec.Containers[kineIndex].Command = []string{"/bin/kine"}
|
||||
podSpec.Containers[kineIndex].Args = utilities.ArgsFromMapToSlice(args)
|
||||
podSpec.Containers[kineIndex].VolumeMounts = []corev1.VolumeMount{
|
||||
{
|
||||
Name: kineVolumeCertName,
|
||||
MountPath: "/certs",
|
||||
ReadOnly: false,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].TerminationMessagePath = corev1.TerminationMessagePathDefault
|
||||
podSpec.Containers[kineIndex].TerminationMessagePolicy = corev1.TerminationMessageReadFile
|
||||
podSpec.Containers[kineIndex].Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "GODEBUG",
|
||||
Value: "x509ignoreCN=0",
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].EnvFrom = []corev1.EnvFromSource{
|
||||
{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: tcp.Status.Storage.Kine.Config.SecretName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].Ports = []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: 2379,
|
||||
Name: "server",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].ImagePullPolicy = corev1.PullAlways
|
||||
}
|
||||
|
||||
func (d *Deployment) SetSelector(deploymentSpec *appsv1.DeploymentSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
|
||||
@@ -30,10 +30,8 @@ const (
|
||||
defaultETCDClientSecretName = "root-client-certs"
|
||||
defaultETCDClientSecretNamespace = "kamaji-system"
|
||||
defaultTmpDirectory = "/tmp/kamaji"
|
||||
defaultKineMySQLSecretName = "mysql-config"
|
||||
defaultKineMySQLSecretNamespace = "kamaji-system"
|
||||
defaultKineMySQLHost = "localhost"
|
||||
defaultKineMySQLPort = 3306
|
||||
defaultKineSecretName = "kine-secret"
|
||||
defaultKineSecretNamespace = "kamaji-system"
|
||||
defaultKineImage = "rancher/kine:v0.9.2-amd64"
|
||||
)
|
||||
|
||||
@@ -46,7 +44,7 @@ func InitConfig() (*viper.Viper, error) {
|
||||
flag.String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.Bool("leader-elect", false, "Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.String("etcd-storage-type", defaultETCDStorageType, "Type of storage for ETCD (i.e etcd, kine-mysql, kine-postgres)")
|
||||
flag.String("etcd-storage-type", defaultETCDStorageType, "Type of storage for ETCD (i.e etcd, kine-mysql, kine-psql)")
|
||||
flag.String("etcd-ca-secret-name", defaultETCDCASecretName, "Name of the secret which contains CA's certificate and private key.")
|
||||
flag.String("etcd-ca-secret-namespace", defaultETCDCASecretNamespace, "Namespace of the secret which contains CA's certificate and private key.")
|
||||
flag.String("etcd-client-secret-name", defaultETCDClientSecretName, "Name of the secret which contains ETCD client certificates")
|
||||
@@ -54,10 +52,10 @@ func InitConfig() (*viper.Viper, error) {
|
||||
flag.String("etcd-endpoints", defaultETCDEndpoints, "Comma-separated list with ETCD endpoints (i.e. https://etcd-0.etcd.kamaji-system.svc.cluster.local,https://etcd-1.etcd.kamaji-system.svc.cluster.local,https://etcd-2.etcd.kamaji-system.svc.cluster.local)")
|
||||
flag.String("etcd-compaction-interval", defaultETCDCompactionInterval, "ETCD Compaction interval (i.e. \"5m0s\"). (default: \"0\" (disabled))")
|
||||
flag.String("tmp-directory", defaultTmpDirectory, "Directory which will be used to work with temporary files.")
|
||||
flag.String("kine-mysql-secret-name", defaultKineMySQLSecretName, "Name of the secret which contains MySQL (Kine) configuration.")
|
||||
flag.String("kine-mysql-secret-namespace", defaultKineMySQLSecretNamespace, "Name of the namespace where the secret which contains MySQL (Kine) configuration.")
|
||||
flag.String("kine-mysql-host", defaultKineMySQLHost, "Host where MySQL (Kine) is working")
|
||||
flag.Int("kine-mysql-port", defaultKineMySQLPort, "Port where MySQL (Kine) is working")
|
||||
flag.String("kine-secret-name", defaultKineSecretName, "Name of the secret which contains the Kine configuration.")
|
||||
flag.String("kine-secret-namespace", defaultKineSecretNamespace, "Name of the namespace where the secret which contains the Kine configuration.")
|
||||
flag.String("kine-host", "", "Host where the DB used by Kine is working.")
|
||||
flag.Int("kine-port", 0, "Port where the DB used by Kine is listening to.")
|
||||
flag.String("kine-image", defaultKineImage, "Container image along with tag to use for the Kine sidecar container (used only if etcd-storage-type is set to one of kine strategies)")
|
||||
|
||||
// Setup zap configuration
|
||||
@@ -110,16 +108,16 @@ func InitConfig() (*viper.Viper, error) {
|
||||
if err := config.BindEnv("tmp-directory", fmt.Sprintf("%s_TMP_DIRECTORY", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-mysql-secret-name", fmt.Sprintf("%s_KINE_MYSQL_SECRET_NAME", envPrefix)); err != nil {
|
||||
if err := config.BindEnv("kine-secret-name", fmt.Sprintf("%s_KINE_SECRET_NAME", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-mysql-secret-namespace", fmt.Sprintf("%s_KINE_MYSQL_SECRET_NAMESPACE", envPrefix)); err != nil {
|
||||
if err := config.BindEnv("kine-secret-namespace", fmt.Sprintf("%s_KINE_SECRET_NAMESPACE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-mysql-host", fmt.Sprintf("%s_KINE_MYSQL_HOST", envPrefix)); err != nil {
|
||||
if err := config.BindEnv("kine-host", fmt.Sprintf("%s_KINE_HOST", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-mysql-port", fmt.Sprintf("%s_KINE_MYSQL_PORT", envPrefix)); err != nil {
|
||||
if err := config.BindEnv("kine-port", fmt.Sprintf("%s_KINE_PORT", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-image", fmt.Sprintf("%s_KINE_IMAGE", envPrefix)); err != nil {
|
||||
|
||||
@@ -29,8 +29,8 @@ type SQLCertificate struct {
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Storage.KineMySQL.Certificate.SecretName != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Certificate.ResourceVersion != r.resource.ResourceVersion
|
||||
return tenantControlPlane.Status.Storage.Kine.Certificate.SecretName != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Storage.Kine.Certificate.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
@@ -70,13 +70,13 @@ func (r *SQLCertificate) GetName() string {
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL == nil {
|
||||
tenantControlPlane.Status.Storage.KineMySQL = &kamajiv1alpha1.KineMySQLStatus{}
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
tenantControlPlane.Status.Storage.Kine = &kamajiv1alpha1.KineStatus{}
|
||||
}
|
||||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Certificate.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Certificate.ResourceVersion = r.resource.ResourceVersion
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Certificate.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Storage.Kine.Certificate.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Storage.Kine.Certificate.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
tenantControlPlane.Status.Storage.Kine.Certificate.LastUpdate = metav1.Now()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -89,10 +89,20 @@ func (r *SQLCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.buildSecret(ctx, *sqlConfig); err != nil {
|
||||
checksum, err := r.buildSecret(ctx, *sqlConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
annotations := r.resource.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
annotations["checksum"] = checksum
|
||||
|
||||
r.resource.SetAnnotations(annotations)
|
||||
|
||||
r.resource.SetLabels(utilities.MergeMaps(
|
||||
utilities.KamajiLabels(),
|
||||
r.resource.GetLabels(),
|
||||
@@ -106,26 +116,29 @@ func (r *SQLCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) buildSecret(ctx context.Context, sqlConfig corev1.Secret) error {
|
||||
func (r *SQLCertificate) buildSecret(ctx context.Context, sqlConfig corev1.Secret) (checksum string, err error) {
|
||||
switch r.StorageType {
|
||||
case types.KineMySQL:
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
keys := []string{"ca.crt", "server.crt", "server.key"}
|
||||
|
||||
return r.buildKineSecret(ctx, keys, sqlConfig)
|
||||
default:
|
||||
return fmt.Errorf("storage type %s is not implemented", r.StorageType)
|
||||
return "", fmt.Errorf("storage type %s is not implemented", r.StorageType)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) buildKineSecret(ctx context.Context, keys []string, sqlConfig corev1.Secret) error {
|
||||
func (r *SQLCertificate) buildKineSecret(ctx context.Context, keys []string, sqlConfig corev1.Secret) (string, error) {
|
||||
checksumMap := map[string]string{}
|
||||
|
||||
for _, key := range keys {
|
||||
value, ok := sqlConfig.Data[key]
|
||||
if !ok {
|
||||
return fmt.Errorf("%s is not in sql config secret", key)
|
||||
return "", fmt.Errorf("%s is not in sql config secret", key)
|
||||
}
|
||||
|
||||
r.resource.Data[key] = value
|
||||
checksumMap[key] = string(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
return utilities.CalculateConfigMapChecksum(checksumMap), nil
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ import (
|
||||
"github.com/clastix/kamaji/internal/sql"
|
||||
)
|
||||
|
||||
const (
|
||||
secretHashLabelKey = "component.kamaji.clastix.io/secret-hash"
|
||||
)
|
||||
|
||||
type sqlSetupResource struct {
|
||||
schema string
|
||||
user string
|
||||
@@ -35,13 +31,13 @@ type SQLSetup struct {
|
||||
}
|
||||
|
||||
func (r *SQLSetup) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL == nil {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return tenantControlPlane.Status.Storage.KineMySQL.Setup.Schema != r.resource.schema ||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.User != r.resource.user ||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.SQLConfigResourceVersion != tenantControlPlane.Status.Storage.KineMySQL.Config.ResourceVersion
|
||||
return tenantControlPlane.Status.Storage.Kine.Setup.Schema != r.resource.schema ||
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.User != r.resource.user ||
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.Checksum != tenantControlPlane.Status.Storage.Kine.Config.Checksum
|
||||
}
|
||||
|
||||
func (r *SQLSetup) ShouldCleanup(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
@@ -56,16 +52,16 @@ func (r *SQLSetup) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha
|
||||
secret := &corev1.Secret{}
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: tenantControlPlane.GetNamespace(),
|
||||
Name: tenantControlPlane.Status.Storage.KineMySQL.Config.SecretName,
|
||||
Name: tenantControlPlane.Status.Storage.Kine.Config.SecretName,
|
||||
}
|
||||
if err := r.Client.Get(ctx, namespacedName, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.resource = sqlSetupResource{
|
||||
schema: string(secret.Data["MYSQL_SCHEMA"]),
|
||||
user: string(secret.Data["MYSQL_USER"]),
|
||||
password: string(secret.Data["MYSQL_PASSWORD"]),
|
||||
schema: string(secret.Data["DB_SCHEMA"]),
|
||||
user: string(secret.Data["DB_USER"]),
|
||||
password: string(secret.Data["DB_PASSWORD"]),
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -76,8 +72,8 @@ func (r *SQLSetup) GetClient() client.Client {
|
||||
}
|
||||
|
||||
func (r *SQLSetup) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL.Setup.SQLConfigResourceVersion != "" &&
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.SQLConfigResourceVersion != tenantControlPlane.Status.Storage.KineMySQL.Config.ResourceVersion {
|
||||
if tenantControlPlane.Status.Storage.Kine.Setup.Checksum != "" &&
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.Checksum != tenantControlPlane.Status.Storage.Kine.Config.Checksum {
|
||||
if err := r.Delete(ctx, tenantControlPlane); err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
@@ -135,14 +131,14 @@ func (r *SQLSetup) Delete(ctx context.Context, tenantControlPlane *kamajiv1alpha
|
||||
}
|
||||
|
||||
func (r *SQLSetup) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL == nil {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return fmt.Errorf("sql configuration is not ready")
|
||||
}
|
||||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.Schema = r.resource.schema
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.User = r.resource.user
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Setup.SQLConfigResourceVersion = tenantControlPlane.Status.Storage.KineMySQL.Config.ResourceVersion
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.Schema = r.resource.schema
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.User = r.resource.user
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.Checksum = tenantControlPlane.Status.Storage.Kine.Config.Checksum
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -23,15 +23,17 @@ type SQLStorageConfig struct {
|
||||
Name string
|
||||
Host string
|
||||
Port int
|
||||
Driver string
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL == nil {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return tenantControlPlane.Status.Storage.KineMySQL.Config.SecretName != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Config.ResourceVersion != r.resource.ResourceVersion
|
||||
return tenantControlPlane.Status.Storage.Kine.Config.SecretName != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Storage.Kine.Config.Checksum != r.resource.GetAnnotations()["checksum"] ||
|
||||
tenantControlPlane.Status.Storage.Kine.Driver != r.Driver
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
@@ -70,42 +72,50 @@ func (r *SQLStorageConfig) GetName() string {
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.KineMySQL == nil {
|
||||
tenantControlPlane.Status.Storage.KineMySQL = &kamajiv1alpha1.KineMySQLStatus{}
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
tenantControlPlane.Status.Storage.Kine = &kamajiv1alpha1.KineStatus{}
|
||||
}
|
||||
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Config.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Storage.KineMySQL.Config.ResourceVersion = r.resource.ResourceVersion
|
||||
tenantControlPlane.Status.Storage.Kine.Driver = r.Driver
|
||||
tenantControlPlane.Status.Storage.Kine.Config.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Storage.Kine.Config.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
calculatedHash := utilities.HashValue(*r.resource)
|
||||
savedHash, ok := r.resource.GetLabels()[secretHashLabelKey]
|
||||
|
||||
var password []byte
|
||||
if ok && calculatedHash == savedHash {
|
||||
password = r.resource.Data["MYSQL_PASSWORD"]
|
||||
} else {
|
||||
|
||||
savedHash, ok := r.resource.GetAnnotations()["checksum"]
|
||||
switch {
|
||||
case ok && savedHash == utilities.CalculateConfigMapChecksum(r.resource.StringData):
|
||||
password = r.resource.Data["DB_PASSWORD"]
|
||||
default:
|
||||
password = []byte(utilities.GenerateUUIDString())
|
||||
}
|
||||
|
||||
r.resource.Data = map[string][]byte{
|
||||
"MYSQL_HOST": []byte(r.Host),
|
||||
"MYSQL_PORT": []byte(strconv.Itoa(r.Port)),
|
||||
"MYSQL_SCHEMA": []byte(tenantControlPlane.GetName()),
|
||||
"MYSQL_USER": []byte(tenantControlPlane.GetName()),
|
||||
"MYSQL_PASSWORD": password,
|
||||
"DB_HOST": []byte(r.Host),
|
||||
"DB_PORT": []byte(strconv.Itoa(r.Port)),
|
||||
"DB_SCHEMA": []byte(tenantControlPlane.GetName()),
|
||||
"DB_USER": []byte(tenantControlPlane.GetName()),
|
||||
"DB_PASSWORD": password,
|
||||
}
|
||||
|
||||
annotations := r.resource.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
annotations["checksum"] = utilities.CalculateConfigMapChecksum(r.resource.StringData)
|
||||
r.resource.SetAnnotations(annotations)
|
||||
|
||||
r.resource.SetLabels(utilities.MergeMaps(
|
||||
utilities.KamajiLabels(),
|
||||
map[string]string{
|
||||
"kamaji.clastix.io/name": tenantControlPlane.GetName(),
|
||||
"kamaji.clastix.io/component": r.GetName(),
|
||||
secretHashLabelKey: utilities.HashValue(*r.resource),
|
||||
},
|
||||
))
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
@@ -29,6 +30,22 @@ type MySQLConnection struct {
|
||||
port int
|
||||
}
|
||||
|
||||
func (c *MySQLConnection) Driver() string {
|
||||
return "MySQL"
|
||||
}
|
||||
|
||||
func getPostgreSQLDB(config ConnectionConfig) (DBConnection, error) {
|
||||
opt := &pg.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", config.Host, config.Port),
|
||||
Database: config.DBName,
|
||||
User: config.User,
|
||||
Password: config.Password,
|
||||
TLSConfig: config.TLSConfig,
|
||||
}
|
||||
|
||||
return &PostgreSQLConnection{db: pg.Connect(opt), port: config.Port, host: config.Host}, nil
|
||||
}
|
||||
|
||||
func getMySQLDB(config ConnectionConfig) (DBConnection, error) {
|
||||
tlsKey := "mysql"
|
||||
dataSourceName := config.GetDataSourceName()
|
||||
|
||||
126
internal/sql/postgresql.go
Normal file
126
internal/sql/postgresql.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-pg/pg/v10"
|
||||
)
|
||||
|
||||
const (
|
||||
postgresqlFetchDBStatement = "SELECT FROM pg_database WHERE datname = ?"
|
||||
postgresqlCreateDBStatement = "CREATE DATABASE %s"
|
||||
postgresqlUserExists = "SELECT 1 FROM pg_roles WHERE rolname = ?"
|
||||
postgresqlCreateUserStatement = "CREATE ROLE %s LOGIN PASSWORD ?"
|
||||
postgresqlShowGrantsStatement = "SELECT has_database_privilege(rolname, ?, 'create') from pg_roles where rolcanlogin and rolname = ?"
|
||||
postgresqlGrantPrivilegesStatement = "GRANT ALL PRIVILEGES ON DATABASE %s TO %s"
|
||||
postgresqlRevokePrivilegesStatement = "REVOKE ALL PRIVILEGES ON DATABASE %s FROM %s"
|
||||
postgresqlDropRoleStatement = "DROP ROLE %s"
|
||||
postgresqlDropDBStatement = "DROP DATABASE %s WITH (FORCE)"
|
||||
)
|
||||
|
||||
type PostgreSQLConnection struct {
|
||||
db *pg.DB
|
||||
host string
|
||||
port int
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) Driver() string {
|
||||
return "PostgreSQL"
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) UserExists(ctx context.Context, user string) (bool, error) {
|
||||
res, err := r.db.ExecContext(ctx, postgresqlUserExists, user)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return res.RowsReturned() > 0, nil
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) CreateUser(ctx context.Context, user, password string) error {
|
||||
_, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlCreateUserStatement, user), password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) DBExists(ctx context.Context, dbName string) (bool, error) {
|
||||
rows, err := r.db.ExecContext(ctx, postgresqlFetchDBStatement, dbName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return rows.RowsReturned() > 0, nil
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) CreateDB(ctx context.Context, dbName string) error {
|
||||
_, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlCreateDBStatement, dbName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) GrantPrivilegesExists(ctx context.Context, user, dbName string) (bool, error) {
|
||||
var hasDatabasePrivilege string
|
||||
|
||||
_, err := r.db.QueryContext(ctx, pg.Scan(&hasDatabasePrivilege), postgresqlShowGrantsStatement, dbName, user)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "does not exist") {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
return hasDatabasePrivilege == "t", nil
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) GrantPrivileges(ctx context.Context, user, dbName string) error {
|
||||
res, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlGrantPrivilegesStatement, dbName, user))
|
||||
_ = res
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) DeleteUser(ctx context.Context, user string) error {
|
||||
_, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlDropRoleStatement, user))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) DeleteDB(ctx context.Context, dbName string) error {
|
||||
_, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlDropDBStatement, dbName))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) RevokePrivileges(ctx context.Context, user, dbName string) error {
|
||||
_, err := r.db.ExecContext(ctx, fmt.Sprintf(postgresqlRevokePrivilegesStatement, dbName, user))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) GetHost() string {
|
||||
return r.host
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) GetPort() int {
|
||||
return r.port
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) Close() error {
|
||||
return r.db.Close()
|
||||
}
|
||||
|
||||
func (r *PostgreSQLConnection) Check() error {
|
||||
return r.db.Ping(context.Background())
|
||||
}
|
||||
@@ -14,12 +14,15 @@ type Driver int
|
||||
|
||||
const (
|
||||
MySQL Driver = iota
|
||||
PostgreSQL
|
||||
)
|
||||
|
||||
func (d Driver) ToString() string {
|
||||
switch d {
|
||||
case MySQL:
|
||||
return "mysql"
|
||||
case PostgreSQL:
|
||||
return "postgresql"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -88,12 +91,15 @@ type DBConnection interface {
|
||||
GetPort() int
|
||||
Close() error
|
||||
Check() error
|
||||
Driver() string
|
||||
}
|
||||
|
||||
func GetDBConnection(config ConnectionConfig) (DBConnection, error) {
|
||||
switch config.SQLDriver {
|
||||
case MySQL:
|
||||
return getMySQLDB(config)
|
||||
case PostgreSQL:
|
||||
return getPostgreSQLDB(config)
|
||||
default:
|
||||
return nil, fmt.Errorf("%s is not a valid driver", config.SQLDriver.ToString())
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ type ETCDStorageType int
|
||||
const (
|
||||
ETCD ETCDStorageType = iota
|
||||
KineMySQL
|
||||
KinePostgreSQL
|
||||
)
|
||||
|
||||
var etcdStorageTypeString = map[string]ETCDStorageType{"etcd": ETCD, "kine-mysql": KineMySQL}
|
||||
var etcdStorageTypeString = map[string]ETCDStorageType{"etcd": ETCD, "kine-mysql": KineMySQL, "kine-postgresql": KinePostgreSQL}
|
||||
|
||||
func (s ETCDStorageType) String() string {
|
||||
return [...]string{"etcd", "kine-mysql"}[s]
|
||||
return [...]string{"etcd", "kine-mysql", "kine-postgresql"}[s]
|
||||
}
|
||||
|
||||
// ParseETCDStorageType returns the ETCDStorageType given a string representation of the type.
|
||||
@@ -36,7 +37,7 @@ func ParseETCDEndpoint(conf *viper.Viper) string {
|
||||
switch ParseETCDStorageType(conf.GetString("etcd-storage-type")) {
|
||||
case ETCD:
|
||||
return conf.GetString("etcd-endpoints")
|
||||
case KineMySQL:
|
||||
case KineMySQL, KinePostgreSQL:
|
||||
return "127.0.0.1:2379"
|
||||
default:
|
||||
panic("unsupported storage type")
|
||||
|
||||
8
main.go
8
main.go
@@ -72,10 +72,10 @@ func main() {
|
||||
ETCDEndpoints: types.ParseETCDEndpoint(conf),
|
||||
ETCDCompactionInterval: conf.GetString("etcd-compaction-interval"),
|
||||
TmpBaseDirectory: conf.GetString("tmp-directory"),
|
||||
KineMySQLSecretName: conf.GetString("kine-mysql-secret-name"),
|
||||
KineMySQLSecretNamespace: conf.GetString("kine-mysql-secret-namespace"),
|
||||
KineMySQLHost: conf.GetString("kine-mysql-host"),
|
||||
KineMySQLPort: conf.GetInt("kine-mysql-port"),
|
||||
KineSecretName: conf.GetString("kine-secret-name"),
|
||||
KineSecretNamespace: conf.GetString("kine-secret-namespace"),
|
||||
KineHost: conf.GetString("kine-host"),
|
||||
KinePort: conf.GetInt("kine-port"),
|
||||
KineContainerImage: conf.GetString("kine-image"),
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user