mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-04 02:41:24 +02:00
Use ConfigMap to store operator's config
This commit is contained in:
+48
-39
@@ -2,64 +2,53 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.bus.zalan.do/acid/postgres-operator/pkg/spec"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
)
|
||||
|
||||
type TPR struct {
|
||||
ReadyWaitInterval time.Duration `split_words:"true" default:"3s"`
|
||||
ReadyWaitTimeout time.Duration `split_words:"true" default:"30s"`
|
||||
ResyncPeriod time.Duration `split_words:"true" default:"5m"`
|
||||
ReadyWaitInterval time.Duration `name:"ready_wait_interval" default:"4s"`
|
||||
ReadyWaitTimeout time.Duration `name:"ready_wait_timeout" default:"30s"`
|
||||
ResyncPeriod time.Duration `name:"resync_period" default:"5m"`
|
||||
}
|
||||
|
||||
type Resources struct {
|
||||
ResyncPeriodPod time.Duration `split_words:"true" default:"5m"`
|
||||
ResourceCheckInterval time.Duration `split_words:"true" default:"3s"`
|
||||
ResourceCheckTimeout time.Duration `split_words:"true" default:"10m"`
|
||||
PodLabelWaitTimeout time.Duration `split_words:"true" default:"10m"`
|
||||
PodDeletionWaitTimeout time.Duration `split_words:"true" default:"10m"`
|
||||
ResyncPeriodPod time.Duration `name:"resync_period_pod" default:"5m"`
|
||||
ResourceCheckInterval time.Duration `name:"resource_check_interval" default:"3s"`
|
||||
ResourceCheckTimeout time.Duration `name:"resource_check_timeout" default:"10m"`
|
||||
PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"`
|
||||
PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
|
||||
ClusterLabels map[string]string `name:"cluster_labels" default:"application:spilo"`
|
||||
ClusterNameLabel string `name:"cluster_name_label" default:"cluster-name"`
|
||||
PodRoleLabel string `name:"pod_role_label" default:"spilo-role"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
PamRoleName string `split_words:"true" default:"zalandos"`
|
||||
PamConfiguration string `split_words:"true" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"`
|
||||
TeamsAPIUrl string `envconfig:"teams_api_url" default:"https://teams.example.com/api/"`
|
||||
OAuthTokenSecretName spec.NamespacedName `envconfig:"oauth_token_secret_name" default:"postgresql-operator"`
|
||||
InfrastructureRolesSecretName spec.NamespacedName `split_words:"true"`
|
||||
SuperUsername string `split_words:"true" default:"postgres"`
|
||||
ReplicationUsername string `split_words:"true" default:"replication"`
|
||||
PamRoleName string `name:"pam_rol_name" default:"zalandos"`
|
||||
PamConfiguration string `name:"pam_configuration" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"`
|
||||
TeamsAPIUrl string `name:"teams_api_url" default:"https://teams.example.com/api/"`
|
||||
OAuthTokenSecretName spec.NamespacedName `name:"oauth_token_secret_name" default:"postgresql-operator"`
|
||||
InfrastructureRolesSecretName spec.NamespacedName `name:"infrastructure_roles_secret_name"`
|
||||
SuperUsername string `name:"super_username" default:"postgres"`
|
||||
ReplicationUsername string `name:"replication_username" default:"replication"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
TPR
|
||||
Resources
|
||||
Auth
|
||||
EtcdHost string `split_words:"true" default:"etcd-client.default.svc.cluster.local:2379"`
|
||||
DockerImage string `split_words:"true" default:"registry.opensource.zalan.do/acid/spilo-9.6:1.2-p12"`
|
||||
ServiceAccountName string `split_words:"true" default:"operator"`
|
||||
DbHostedZone string `split_words:"true" default:"db.example.com"`
|
||||
EtcdScope string `split_words:"true" default:"service"`
|
||||
WALES3Bucket string `envconfig:"wal_s3_bucket"`
|
||||
KubeIAMRole string `envconfig:"kube_iam_role"`
|
||||
DebugLogging bool `split_words:"true" default:"false"`
|
||||
DNSNameFormat string `envconfig:"dns_name_format" default:"%s.%s.%s"`
|
||||
}
|
||||
|
||||
func LoadFromEnv() *Config {
|
||||
//TODO: maybe we should use ConfigMaps( https://kubernetes.io/docs/tasks/configure-pod-container/configmap/ ) instead?
|
||||
|
||||
var cfg Config
|
||||
err := envconfig.Process("PGOP", &cfg)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Can't read config: %v", err))
|
||||
}
|
||||
cfg.EtcdScope = strings.Trim(cfg.EtcdScope, "/")
|
||||
|
||||
return &cfg
|
||||
Namespace string `name:"namespace"`
|
||||
EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"`
|
||||
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-9.6:1.2-p12"`
|
||||
ServiceAccountName string `name:"service_account_name" default:"operator"`
|
||||
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
||||
EtcdScope string `name:"etcd_scope" default:"service"`
|
||||
WALES3Bucket string `name:"wal_s3_bucket"`
|
||||
KubeIAMRole string `name:"kube_iam_role"`
|
||||
DebugLogging bool `name:"debug_logging" default:"false"`
|
||||
DNSNameFormat string `name:"dns_name_format" default:"%s.%s.%s"`
|
||||
}
|
||||
|
||||
func (c Config) MustMarshal() string {
|
||||
@@ -70,3 +59,23 @@ func (c Config) MustMarshal() string {
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func NewFromMap(m map[string]string) *Config {
|
||||
cfg := Config{}
|
||||
fields, _ := structFields(&cfg)
|
||||
|
||||
for _, structField := range fields {
|
||||
key := strings.ToLower(structField.Name)
|
||||
value, ok := m[key]
|
||||
if !ok && structField.Default != "" {
|
||||
value = structField.Default
|
||||
}
|
||||
|
||||
err := processField(value, structField.Field)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Decoder interface {
|
||||
Decode(value string) error
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
Name string
|
||||
Default string
|
||||
Field reflect.Value
|
||||
}
|
||||
|
||||
func decoderFrom(field reflect.Value) (d Decoder) {
|
||||
// it may be impossible for a struct field to fail this check
|
||||
if !field.CanInterface() {
|
||||
return
|
||||
}
|
||||
|
||||
d, ok := field.Interface().(Decoder)
|
||||
if !ok && field.CanAddr() {
|
||||
d, ok = field.Addr().Interface().(Decoder)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// taken from github.com/kelseyhightower/envconfig
|
||||
func structFields(spec interface{}) ([]fieldInfo, error) {
|
||||
s := reflect.ValueOf(spec).Elem()
|
||||
|
||||
// over allocate an info array, we will extend if needed later
|
||||
infos := make([]fieldInfo, 0, s.NumField())
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
ftype := s.Type().Field(i)
|
||||
|
||||
fieldName := ftype.Tag.Get("name")
|
||||
if fieldName == "" {
|
||||
fieldName = strings.ToLower(ftype.Name)
|
||||
}
|
||||
|
||||
// Capture information about the config variable
|
||||
info := fieldInfo{
|
||||
Name: fieldName,
|
||||
Field: f,
|
||||
Default: ftype.Tag.Get("default"),
|
||||
}
|
||||
infos = append(infos, info)
|
||||
|
||||
if f.Kind() == reflect.Struct {
|
||||
// honor Decode if present
|
||||
if decoderFrom(f) == nil {
|
||||
embeddedPtr := f.Addr().Interface()
|
||||
embeddedInfos, err := structFields(embeddedPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos = append(infos[:len(infos)-1], embeddedInfos...)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func processField(value string, field reflect.Value) error {
|
||||
typ := field.Type()
|
||||
|
||||
decoder := decoderFrom(field)
|
||||
if decoder != nil {
|
||||
return decoder.Decode(value)
|
||||
}
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(typ))
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
field.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var (
|
||||
val int64
|
||||
err error
|
||||
)
|
||||
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err = time.ParseDuration(value)
|
||||
val = int64(d)
|
||||
} else {
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.SetInt(val)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val, err := strconv.ParseUint(value, 0, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetUint(val)
|
||||
case reflect.Bool:
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetBool(val)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val, err := strconv.ParseFloat(value, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetFloat(val)
|
||||
case reflect.Slice:
|
||||
vals := strings.Split(value, ",")
|
||||
sl := reflect.MakeSlice(typ, len(vals), len(vals))
|
||||
for i, val := range vals {
|
||||
err := processField(val, sl.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
field.Set(sl)
|
||||
case reflect.Map:
|
||||
pairs := strings.Split(value, ",")
|
||||
mp := reflect.MakeMap(typ)
|
||||
for _, pair := range pairs {
|
||||
kvpair := strings.Split(pair, ":")
|
||||
if len(kvpair) != 2 {
|
||||
return fmt.Errorf("invalid map item: %q", pair)
|
||||
}
|
||||
k := reflect.New(typ.Key()).Elem()
|
||||
err := processField(kvpair[0], k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := reflect.New(typ.Elem()).Elem()
|
||||
err = processField(kvpair[1], v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mp.SetMapIndex(k, v)
|
||||
}
|
||||
field.Set(mp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2,20 +2,16 @@ package constants
|
||||
|
||||
const (
|
||||
//Constants
|
||||
TPRName = "postgresql"
|
||||
TPRVendor = "acid.zalan.do"
|
||||
TPRDescription = "Managed PostgreSQL clusters"
|
||||
TPRApiVersion = "v1"
|
||||
DataVolumeName = "pgdata"
|
||||
PasswordLength = 64
|
||||
UserSecretTemplate = "%s.%s.credentials.%s.%s" // Username, ClusterName, TPRName, TPRVendor
|
||||
ZalandoDnsNameAnnotation = "zalando.org/dnsname"
|
||||
KubeIAmAnnotation = "iam.amazonaws.com/role"
|
||||
ResourceName = TPRName + "s"
|
||||
PodRoleMaster = "master"
|
||||
PodRoleReplica = "replica"
|
||||
ApplicationNameLabel = "application"
|
||||
ApplicationNameLabelValue = "spilo"
|
||||
SpiloRoleLabel = "spilo-role"
|
||||
ClusterNameLabel = "version"
|
||||
TPRName = "postgresql"
|
||||
TPRVendor = "acid.zalan.do"
|
||||
TPRDescription = "Managed PostgreSQL clusters"
|
||||
TPRApiVersion = "v1"
|
||||
DataVolumeName = "pgdata"
|
||||
PasswordLength = 64
|
||||
UserSecretTemplate = "%s.%s.credentials.%s.%s" // Username, ClusterName, TPRName, TPRVendor
|
||||
ZalandoDnsNameAnnotation = "zalando.org/dnsname"
|
||||
KubeIAmAnnotation = "iam.amazonaws.com/role"
|
||||
ResourceName = TPRName + "s"
|
||||
PodRoleMaster = "master"
|
||||
PodRoleReplica = "replica"
|
||||
)
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"github.bus.zalan.do/acid/postgres-operator/pkg/spec"
|
||||
"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants"
|
||||
)
|
||||
|
||||
var passwordChars = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
@@ -37,21 +36,6 @@ func NameFromMeta(meta v1.ObjectMeta) spec.NamespacedName {
|
||||
}
|
||||
}
|
||||
|
||||
func PodClusterName(pod *v1.Pod) spec.NamespacedName {
|
||||
if name, ok := pod.Labels[constants.ClusterNameLabel]; ok {
|
||||
return spec.NamespacedName{
|
||||
Namespace: pod.Namespace,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
return spec.NamespacedName{}
|
||||
}
|
||||
|
||||
func PodSpiloRole(pod *v1.Pod) string {
|
||||
return pod.Labels[constants.SpiloRoleLabel]
|
||||
}
|
||||
|
||||
func PGUserPassword(user spec.PgUser) string {
|
||||
s := md5.Sum([]byte(user.Password + user.Name))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user