feat: postgresql kine driver

This commit is contained in:
Dario Tranchitella
2022-08-20 11:32:51 +02:00
parent 06504e7133
commit 31b25f7c78
15 changed files with 444 additions and 160 deletions

View File

@@ -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:

View File

@@ -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},
},
},
)
}

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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),
},
))

View File

@@ -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
View 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())
}

View File

@@ -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())
}

View File

@@ -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")

View File

@@ -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"),
},
}