Allow configuring the operator via the YAML manifest. (#326)

* Up until now, the operator read its own configuration from the
configmap.  That has a number of limitations, i.e. when the
configuration value is not a scalar, but a map or a list. We use a
custom code based on github.com/kelseyhightower/envconfig to decode
non-scalar values out of plain text keys, but that breaks when the data
inside the keys contains both YAML-special elememtns (i.e. commas) and
complex quotes, one good example for that is search_path inside
`team_api_role_configuration`. In addition, reliance on the configmap
forced a flag structure on the configuration, making it hard to write
and to read (see
https://github.com/zalando-incubator/postgres-operator/pull/308#issuecomment-395131778).

The changes allow to supply the operator configuration in a proper YAML
file. That required registering a custom CRD to support the operator
configuration and provide an example at
manifests/postgresql-operator-default-configuration.yaml. At the moment,
both old configmap and the new CRD configuration is supported, so no
compatibility issues, however, in the future I'd like to deprecate the
configmap-based configuration altogether. Contrary to the
configmap-based configuration, the CRD one doesn't embed defaults into
the operator code, however, one can use the
manifests/postgresql-operator-default-configuration.yaml as a starting
point in order to build a custom configuration.

Since previously `ReadyWaitInterval` and `ReadyWaitTimeout` parameters
used to create the CRD were taken from the operator configuration, which
is not possible if the configuration itself is stored in the CRD object,
I've added the ability to specify them as environment variables
`CRD_READY_WAIT_INTERVAL` and `CRD_READY_WAIT_TIMEOUT` respectively.

Per review by @zerg-junior  and  @Jan-M.
This commit is contained in:
Oleksii Kliukin
2018-07-16 16:20:46 +02:00
committed by GitHub
parent e90a01050c
commit 3a9378d3b8
14 changed files with 584 additions and 45 deletions
+2 -2
View File
@@ -150,7 +150,7 @@ var (
)
// Clone makes a deepcopy of the Postgresql structure. The Error field is nulled-out,
// as there is no guaratee that the actual implementation of the error interface
// as there is no guarantee that the actual implementation of the error interface
// will not contain any private fields not-reachable to deepcopy. This should be ok,
// since Error is never read from a Kubernetes object.
func (p *Postgresql) Clone() *Postgresql {
@@ -200,7 +200,7 @@ func (m *MaintenanceWindow) MarshalJSON() ([]byte, error) {
m.EndTime.Format("15:04"))), nil
}
// UnmarshalJSON convets a JSON to the maintenance window definition.
// UnmarshalJSON converts a JSON to the maintenance window definition.
func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error {
var (
got MaintenanceWindow
+48 -4
View File
@@ -2,6 +2,7 @@ package spec
import (
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"log"
@@ -162,10 +163,12 @@ type ControllerConfig struct {
RestConfig *rest.Config `json:"-"`
InfrastructureRoles map[string]PgUser
NoDatabaseAccess bool
NoTeamsAPI bool
ConfigMapName NamespacedName
Namespace string
NoDatabaseAccess bool
NoTeamsAPI bool
CRDReadyWaitInterval time.Duration
CRDReadyWaitTimeout time.Duration
ConfigMapName NamespacedName
Namespace string
}
// cached value for the GetOperatorNamespace
@@ -185,6 +188,19 @@ func (n *NamespacedName) Decode(value string) error {
return n.DecodeWorker(value, GetOperatorNamespace())
}
func (n *NamespacedName) UnmarshalJSON(data []byte) error {
result := NamespacedName{}
var tmp string
if err := json.Unmarshal(data, &tmp); err != nil {
return err
}
if err := result.Decode(tmp); err != nil {
return err
}
*n = result
return nil
}
// DecodeWorker separates the decode logic to (unit) test
// from obtaining the operator namespace that depends on k8s mounting files at runtime
func (n *NamespacedName) DecodeWorker(value, operatorNamespace string) error {
@@ -235,3 +251,31 @@ func GetOperatorNamespace() string {
}
return operatorNamespace
}
type Duration time.Duration
func (d *Duration) UnmarshalJSON(b []byte) error {
var (
v interface{}
err error
)
if err = json.Unmarshal(b, &v); err != nil {
return err
}
switch val := v.(type) {
case string:
t, err := time.ParseDuration(val)
if err != nil {
return err
}
*d = Duration(t)
return nil
case float64:
t := time.Duration(val)
*d = Duration(t)
return nil
default:
return fmt.Errorf("could not recognize type %T as a valid type to unmarshal to Duration", val)
}
return nil
}