fix(pxc): raise haproxy conn limit (#732)
diff --git a/roles/defaults/helpers.go b/roles/defaults/helpers.go
new file mode 100644
index 0000000..a7bd833
--- /dev/null
+++ b/roles/defaults/helpers.go
@@ -0,0 +1,28 @@
+package defaults
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ r *regexp.Regexp
+)
+
+func init() {
+ r, _ = regexp.Compile(`{{ atmosphere_images\['(?P<ImageName>\w+)'] \| vexxhost.kubernetes.docker_image\('ref'\) }}`)
+}
+
+func AssertAtmosphereImage(t *testing.T, expected string, value string) {
+ matches := r.FindStringSubmatch(value)
+ require.Len(t, matches, 2)
+ imageName := matches[1]
+
+ image, err := GetImageByKey(imageName)
+ require.NoError(t, err)
+
+ assert.Equal(t, expected, image)
+}
diff --git a/roles/defaults/vars.go b/roles/defaults/vars.go
new file mode 100644
index 0000000..07b3dc0
--- /dev/null
+++ b/roles/defaults/vars.go
@@ -0,0 +1,27 @@
+package defaults
+
+import (
+ "bytes"
+ _ "embed"
+
+ "github.com/goccy/go-yaml"
+)
+
+var (
+ //go:embed vars/main.yml
+ vars_file []byte
+)
+
+func GetImageByKey(key string) (string, error) {
+ path, err := yaml.PathString("$._atmosphere_images." + key)
+ if err != nil {
+ return "", err
+ }
+
+ var image string
+ if err := path.Read(bytes.NewReader(vars_file), &image); err != nil {
+ return "", err
+ }
+
+ return image, nil
+}
diff --git a/roles/percona_xtradb_cluster/vars/main.yml b/roles/percona_xtradb_cluster/vars/main.yml
index fe7aff7..7836fdb 100644
--- a/roles/percona_xtradb_cluster/vars/main.yml
+++ b/roles/percona_xtradb_cluster/vars/main.yml
@@ -1,5 +1,5 @@
_percona_xtradb_cluster_spec:
- crVersion: "1.10.0"
+ crVersion: "1.12.0"
secretsName: percona-xtradb
pxc:
size: 3
@@ -43,3 +43,48 @@
image: "{{ atmosphere_images['percona_xtradb_cluster_haproxy'] | vexxhost.kubernetes.docker_image('ref') }}"
nodeSelector:
openstack-control-plane: enabled
+ configuration: |
+ global
+ maxconn 8192
+ external-check
+ insecure-fork-wanted
+ stats socket /etc/haproxy/pxc/haproxy.sock mode 600 expose-fd listeners level admin
+
+ defaults
+ default-server init-addr last,libc,none
+ log global
+ mode tcp
+ retries 10
+ timeout client 28800s
+ timeout connect 100500
+ timeout server 28800s
+
+ frontend galera-in
+ bind *:3309 accept-proxy
+ bind *:3306
+ mode tcp
+ option clitcpka
+ default_backend galera-nodes
+
+ frontend galera-admin-in
+ bind *:33062
+ mode tcp
+ option clitcpka
+ default_backend galera-admin-nodes
+
+ frontend galera-replica-in
+ bind *:3307
+ mode tcp
+ option clitcpka
+ default_backend galera-replica-nodes
+
+ frontend galera-mysqlx-in
+ bind *:33060
+ mode tcp
+ option clitcpka
+ default_backend galera-mysqlx-nodes
+
+ frontend stats
+ bind *:8404
+ mode http
+ http-request use-service prometheus-exporter if { path /metrics }
diff --git a/roles/percona_xtradb_cluster/vars_test.go b/roles/percona_xtradb_cluster/vars_test.go
new file mode 100644
index 0000000..0a73722
--- /dev/null
+++ b/roles/percona_xtradb_cluster/vars_test.go
@@ -0,0 +1,168 @@
+package percona_xtradb_cluster
+
+import (
+ _ "embed"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+ "testing"
+
+ "github.com/goccy/go-yaml"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/vexxhost/atmosphere/roles/defaults"
+ "gopkg.in/ini.v1"
+ "helm.sh/helm/v3/pkg/chart/loader"
+ v1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/resource"
+
+ pxc_v1 "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
+)
+
+var (
+ //go:embed vars/main.yml
+ vars_file []byte
+ vars Vars
+)
+
+type Vars struct {
+ PerconaXtraDBClusterSpec pxc_v1.PerconaXtraDBClusterSpec `yaml:"_percona_xtradb_cluster_spec"`
+}
+
+func TestMain(m *testing.M) {
+ t := &testing.T{}
+
+ err := yaml.UnmarshalWithOptions(vars_file, &vars, yaml.Strict(), yaml.UseJSONUnmarshaler())
+ require.NoError(t, err)
+
+ code := m.Run()
+ os.Exit(code)
+}
+
+func TestPerconaXtraDBClusterSpec(t *testing.T) {
+ chart, err := loader.LoadDir("../../charts/pxc-operator")
+ require.NoError(t, err)
+
+ assert.Equal(t, chart.AppVersion(), vars.PerconaXtraDBClusterSpec.CRVersion)
+ assert.Equal(t, "percona-xtradb", vars.PerconaXtraDBClusterSpec.SecretsName)
+}
+
+func TestPerconaXtraDBClusterPXCSpec(t *testing.T) {
+ assert.Equal(t, int32(3), vars.PerconaXtraDBClusterSpec.PXC.Size)
+ assert.Equal(t, true, *vars.PerconaXtraDBClusterSpec.PXC.AutoRecovery)
+ defaults.AssertAtmosphereImage(t, "docker.io/percona/percona-xtradb-cluster:5.7.39-31.61", vars.PerconaXtraDBClusterSpec.PXC.Image)
+
+ assert.Equal(t, map[string]string{
+ "openstack-control-plane": "enabled",
+ }, vars.PerconaXtraDBClusterSpec.PXC.NodeSelector)
+
+ assert.Equal(t, &pxc_v1.VolumeSpec{
+ PersistentVolumeClaim: &v1.PersistentVolumeClaimSpec{
+ Resources: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ "storage": resource.MustParse("160Gi"),
+ },
+ },
+ },
+ }, vars.PerconaXtraDBClusterSpec.PXC.VolumeSpec)
+}
+
+func parsePXCConfiguration(t *testing.T, cfg string) *ini.File {
+ parsed, err := ini.LoadSources(ini.LoadOptions{
+ AllowBooleanKeys: true,
+ }, []byte(cfg))
+ require.NoError(t, err)
+
+ return parsed
+}
+
+func TestPerconaXtraDBClusterPXCConfiguration(t *testing.T) {
+ cfg := parsePXCConfiguration(t, vars.PerconaXtraDBClusterSpec.PXC.Configuration)
+
+ section := cfg.Section("mysqld")
+ assert.Equal(t, 8192, section.Key("max_connections").MustInt())
+ assert.Equal(t, "4096M", section.Key("innodb_buffer_pool_size").String())
+ assert.Equal(t, "16M", section.Key("max_allowed_packet").String())
+ assert.Equal(t, true, section.Key("skip-name-resolve").MustBool())
+}
+
+func TestPerconaXtraDBClusterPXCSidecarSpec(t *testing.T) {
+ sidecar := vars.PerconaXtraDBClusterSpec.PXC.Sidecars[0]
+ assert.Equal(t, "exporter", sidecar.Name)
+ defaults.AssertAtmosphereImage(t, "quay.io/prometheus/mysqld-exporter:v0.14.0", sidecar.Image)
+
+ assert.Equal(t, v1.EnvVar{
+ Name: "MONITOR_PASSWORD",
+ ValueFrom: &v1.EnvVarSource{
+ SecretKeyRef: &v1.SecretKeySelector{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: vars.PerconaXtraDBClusterSpec.SecretsName,
+ },
+ Key: "monitor",
+ },
+ },
+ }, sidecar.Env[0])
+ assert.Equal(t, v1.EnvVar{
+ Name: "DATA_SOURCE_NAME",
+ Value: "monitor:$(MONITOR_PASSWORD)@(localhost:3306)/",
+ }, sidecar.Env[1])
+
+ assert.Equal(t, v1.ContainerPort{
+ Name: "metrics",
+ ContainerPort: 9104,
+ }, sidecar.Ports[0])
+}
+
+func TestPerconaXtraDBClusterHAProxySpec(t *testing.T) {
+ assert.Equal(t, true, vars.PerconaXtraDBClusterSpec.HAProxy.Enabled)
+ assert.Equal(t, int32(3), vars.PerconaXtraDBClusterSpec.HAProxy.Size)
+
+ chart, err := loader.LoadDir("../../charts/pxc-operator")
+ require.NoError(t, err)
+
+ defaults.AssertAtmosphereImage(t,
+ fmt.Sprintf("docker.io/percona/percona-xtradb-cluster-operator:%s-haproxy", chart.AppVersion()),
+ vars.PerconaXtraDBClusterSpec.HAProxy.Image,
+ )
+
+ assert.Equal(t, map[string]string{
+ "openstack-control-plane": "enabled",
+ }, vars.PerconaXtraDBClusterSpec.HAProxy.NodeSelector)
+}
+
+func TestPerconaXtraDBClusterHAProxyConfiguration(t *testing.T) {
+ chart, err := loader.LoadDir("../../charts/pxc-operator")
+ require.NoError(t, err)
+
+ pxcConfig := parsePXCConfiguration(t, vars.PerconaXtraDBClusterSpec.PXC.Configuration)
+ maxConnections := pxcConfig.Section("mysqld").Key("max_connections").MustInt()
+
+ // NOTE(mnaser): Since there is no way of overriding specific values, we pull
+ // the file from the Docker image, replace the maxconn value and
+ // then compare it.
+
+ // Get the default HAproxy configuration
+ configFileUrl := fmt.Sprintf("https://raw.githubusercontent.com/percona/percona-docker/pxc-operator-%s/haproxy/dockerdir/etc/haproxy/haproxy-global.cfg", chart.AppVersion())
+ resp, err := http.Get(configFileUrl)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+ haproxyConfigData, err := io.ReadAll(resp.Body)
+ require.NoError(t, err)
+ haproxyConfig := string(haproxyConfigData)
+
+ // Replace the 4 spaces at the start of each line
+ regex := regexp.MustCompile("(?m)^ ")
+ haproxyConfig = regex.ReplaceAllString(haproxyConfig, "")
+
+ // Replace the maxconn value
+ haproxyConfig = strings.Replace(haproxyConfig, "maxconn 2048", fmt.Sprintf("maxconn %d", maxConnections), 1)
+ assert.Contains(t, haproxyConfig, fmt.Sprintf("maxconn %d", maxConnections))
+
+ assert.Equal(t,
+ haproxyConfig,
+ vars.PerconaXtraDBClusterSpec.HAProxy.Configuration,
+ )
+}