Honor the "spec-by-example" manifest we have.
No longer ignore custom PostgreSQL and Patroni parameters and initdb options. Since all Patroni parameters that are not under initdb or pg_hba are specified as a plain map, there is no way to distinguish those that should go into the bootstrap section from those that should stay in the local configuration. As the example used only bootstrap parameters, currently all such options go into the bootstrap section. Also the initdb options are repsented as a map, while Patroni initdb options are a list of either maps or strings (i.e. "data-checksums" doesn't need an argument). For now, there is a work-around, but in the future we might consider changing the spec.
This commit is contained in:
		
							parent
							
								
									34ae324fe9
								
							
						
					
					
						commit
						ec3f24c3ee
					
				|  | @ -85,7 +85,7 @@ imports: | |||
| - name: github.com/kr/text | ||||
|   version: 7cafcd837844e784b526369c9bce262804aebc60 | ||||
| - name: github.com/lib/pq | ||||
|   version: 2704adc878c21e1329f46f6e56a1c387d788ff94 | ||||
|   version: 0477eb88c5ca4009cb281f13c90633375b6a9987 | ||||
|   subpackages: | ||||
|   - oid | ||||
| - name: github.com/mailru/easyjson | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ package cluster | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"k8s.io/client-go/pkg/api/resource" | ||||
| 	"k8s.io/client-go/pkg/api/v1" | ||||
|  | @ -12,6 +13,36 @@ import ( | |||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	PGBinariesLocationTemplate       = "/usr/lib/postgresql/%s/bin" | ||||
| 	PatroniPGBinariesParameterName   = "pg_bin" | ||||
| 	PatroniPGParametersParameterName = "parameters" | ||||
| ) | ||||
| 
 | ||||
| type pgUser struct { | ||||
| 	Password string   `json:"password"` | ||||
| 	Options  []string `json:"options"` | ||||
| } | ||||
| 
 | ||||
| type PatroniDCS struct { | ||||
| 	TTL                  uint32  `json:"ttl,omitempty"` | ||||
| 	LoopWait             uint32  `json:"loop_wait,omitempty"` | ||||
| 	RetryTimeout         uint32  `json:"retry_timeout,omitempty"` | ||||
| 	MaximumLagOnFailover float32 `json:"maximum_lag_on_failover,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type pgBootstrap struct { | ||||
| 	Initdb []interface{}     `json:"initdb"` | ||||
| 	Users  map[string]pgUser `json:"users"` | ||||
| 	PgHBA  []string          `json:"pg_hba"` | ||||
| 	DCS    PatroniDCS        `json:"dcs,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type spiloConfiguration struct { | ||||
| 	PgLocalConfiguration map[string]interface{} `json:"postgresql"` | ||||
| 	Bootstrap            pgBootstrap            `json:"bootstrap"` | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) resourceRequirements(resources spec.Resources) *v1.ResourceRequirements { | ||||
| 	specRequests := resources.ResourceRequest | ||||
| 	specLimits := resources.ResourceLimits | ||||
|  | @ -37,7 +68,6 @@ func fillResourceList(spec spec.ResourceDescription, defaults spec.ResourceDescr | |||
| 	} else { | ||||
| 		requests[v1.ResourceCPU] = resource.MustParse(defaults.Cpu) | ||||
| 	} | ||||
| 
 | ||||
| 	if spec.Memory != "" { | ||||
| 		requests[v1.ResourceMemory] = resource.MustParse(spec.Memory) | ||||
| 	} else { | ||||
|  | @ -46,7 +76,103 @@ func fillResourceList(spec spec.ResourceDescription, defaults spec.ResourceDescr | |||
| 	return requests | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements, pgVersion string) *v1.PodTemplateSpec { | ||||
| func (c *Cluster) generateSpiloJSONConfiguration(pg *spec.PostgresqlParam, patroni *spec.Patroni) string { | ||||
| 	config := spiloConfiguration{} | ||||
| 
 | ||||
| 	config.Bootstrap = pgBootstrap{} | ||||
| 
 | ||||
| 	config.Bootstrap.Initdb = []interface{}{map[string]string{"auth-host": "md5"}, | ||||
| 		map[string]string{"auth-local": "trust"}} | ||||
| 
 | ||||
| 	// Initdb parameters in the manifest take priority over the default ones
 | ||||
| 	// The whole type switch dance is caused by the ability to specify both
 | ||||
| 	// maps and normal string items in the array of initdb options. We need
 | ||||
| 	// both to convert the initial key-value to strings when necessary, and
 | ||||
| 	// to de-duplicate the options supplied.
 | ||||
| PATRONI_INITDB_PARAMS: | ||||
| 	for k, v := range patroni.InitDB { | ||||
| 		for i, defaultParam := range config.Bootstrap.Initdb { | ||||
| 			switch defaultParam.(type) { | ||||
| 			case map[string]string: | ||||
| 				{ | ||||
| 					for k1 := range defaultParam.(map[string]string) { | ||||
| 						if k1 == k { | ||||
| 							(config.Bootstrap.Initdb[i]).(map[string]string)[k] = v | ||||
| 							continue PATRONI_INITDB_PARAMS | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			case string: | ||||
| 				{ | ||||
| 					if k == v { | ||||
| 						continue PATRONI_INITDB_PARAMS | ||||
| 					} | ||||
| 				} | ||||
| 			default: | ||||
| 				c.logger.Warnf("Unsupported type for initdb configuration item %s: %T", defaultParam) | ||||
| 				continue PATRONI_INITDB_PARAMS | ||||
| 			} | ||||
| 		} | ||||
| 		// The following options are known to have no parameters
 | ||||
| 		if v == "true" { | ||||
| 			switch k { | ||||
| 			case "data-checksums", "debug", "no-locale", "noclean", "nosync", "sync-only": | ||||
| 				config.Bootstrap.Initdb = append(config.Bootstrap.Initdb, k) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		config.Bootstrap.Initdb = append(config.Bootstrap.Initdb, map[string]string{k: v}) | ||||
| 	} | ||||
| 
 | ||||
| 	// pg_hba parameters in the manifest replace the default ones. We cannot
 | ||||
| 	// reasonably merge them automatically, because pg_hba parsing stops on
 | ||||
| 	// a first successfully matched rule.
 | ||||
| 	if len(patroni.PgHba) > 0 { | ||||
| 		config.Bootstrap.PgHBA = patroni.PgHba | ||||
| 	} else { | ||||
| 		config.Bootstrap.PgHBA = []string{ | ||||
| 			"hostnossl all all all reject", | ||||
| 			fmt.Sprintf("hostssl   all +%s all pam", c.OpConfig.PamRoleName), | ||||
| 			"hostssl   all all all md5", | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if patroni.MaximumLagOnFailover >= 0 { | ||||
| 		config.Bootstrap.DCS.MaximumLagOnFailover = patroni.MaximumLagOnFailover | ||||
| 	} | ||||
| 	if patroni.LoopWait != 0 { | ||||
| 		config.Bootstrap.DCS.LoopWait = patroni.LoopWait | ||||
| 	} | ||||
| 	if patroni.RetryTimeout != 0 { | ||||
| 		config.Bootstrap.DCS.RetryTimeout = patroni.RetryTimeout | ||||
| 	} | ||||
| 	if patroni.TTL != 0 { | ||||
| 		config.Bootstrap.DCS.TTL = patroni.TTL | ||||
| 	} | ||||
| 
 | ||||
| 	config.PgLocalConfiguration = make(map[string]interface{}) | ||||
| 	config.PgLocalConfiguration[PatroniPGBinariesParameterName] = fmt.Sprintf(PGBinariesLocationTemplate, pg.PgVersion) | ||||
| 	if len(pg.Parameters) > 0 { | ||||
| 		config.PgLocalConfiguration[PatroniPGParametersParameterName] = pg.Parameters | ||||
| 	} | ||||
| 	config.Bootstrap.Users = map[string]pgUser{ | ||||
| 		c.OpConfig.PamRoleName: { | ||||
| 			Password: "", | ||||
| 			Options:  []string{constants.RoleFlagCreateDB, constants.RoleFlagNoLogin}, | ||||
| 		}, | ||||
| 	} | ||||
| 	result, err := json.Marshal(config) | ||||
| 	if err != nil { | ||||
| 		c.logger.Errorf("Cannot convert spilo configuration into JSON: %s", err) | ||||
| 		return "" | ||||
| 	} | ||||
| 	return string(result) | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements, pgParameters *spec.PostgresqlParam, patroniParameters *spec.Patroni) *v1.PodTemplateSpec { | ||||
| 
 | ||||
| 	spiloConfiguration := c.generateSpiloJSONConfiguration(pgParameters, patroniParameters) | ||||
| 
 | ||||
| 	envVars := []v1.EnvVar{ | ||||
| 		{ | ||||
| 			Name:  "SCOPE", | ||||
|  | @ -104,26 +230,9 @@ func (c *Cluster) genPodTemplate(resourceRequirements *v1.ResourceRequirements, | |||
| 			Name:  "PAM_OAUTH2", | ||||
| 			Value: c.OpConfig.PamConfiguration, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name: "SPILO_CONFIGURATION", | ||||
| 			Value: fmt.Sprintf(` | ||||
| postgresql: | ||||
|   bin_dir: /usr/lib/postgresql/%s/bin | ||||
| bootstrap: | ||||
|   initdb: | ||||
|   - auth-host: md5 | ||||
|   - auth-local: trust | ||||
|   users: | ||||
|     %s: | ||||
|       password: NULL | ||||
|       options: | ||||
|         - createdb | ||||
|         - nologin | ||||
|   pg_hba: | ||||
|   - hostnossl all all all reject | ||||
|   - hostssl   all +%s all pam | ||||
|   - hostssl   all all all md5`, pgVersion, c.OpConfig.PamRoleName, c.OpConfig.PamRoleName), | ||||
| 		}, | ||||
| 	} | ||||
| 	if spiloConfiguration != "" { | ||||
| 		envVars = append(envVars, v1.EnvVar{Name: "SPILO_CONFIGURATION", Value: spiloConfiguration}) | ||||
| 	} | ||||
| 	if c.OpConfig.WALES3Bucket != "" { | ||||
| 		envVars = append(envVars, v1.EnvVar{Name: "WAL_S3_BUCKET", Value: c.OpConfig.WALES3Bucket}) | ||||
|  | @ -183,7 +292,7 @@ bootstrap: | |||
| 
 | ||||
| func (c *Cluster) genStatefulSet(spec spec.PostgresSpec) *v1beta1.StatefulSet { | ||||
| 	resourceRequirements := c.resourceRequirements(spec.Resources) | ||||
| 	podTemplate := c.genPodTemplate(resourceRequirements, spec.PgVersion) | ||||
| 	podTemplate := c.genPodTemplate(resourceRequirements, &spec.PostgresqlParam, &spec.Patroni) | ||||
| 	volumeClaimTemplate := persistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass) | ||||
| 
 | ||||
| 	statefulSet := &v1beta1.StatefulSet{ | ||||
|  |  | |||
|  | @ -5,10 +5,9 @@ import ( | |||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	_ "github.com/lib/pq" | ||||
| 	"github.com/lib/pq" | ||||
| 
 | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/spec" | ||||
| 	"github.com/lib/pq" | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants" | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,11 +6,11 @@ import ( | |||
| 	"k8s.io/client-go/pkg/api" | ||||
| 	"k8s.io/client-go/pkg/api/v1" | ||||
| 	"k8s.io/client-go/pkg/apis/apps/v1beta1" | ||||
| 	 | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util" | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/k8sutil" | ||||
| 
 | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/spec" | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util" | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/constants" | ||||
| 	"github.bus.zalan.do/acid/postgres-operator/pkg/util/k8sutil" | ||||
| ) | ||||
| 
 | ||||
| func (c *Cluster) loadResources() error { | ||||
|  | @ -185,7 +185,7 @@ func (c *Cluster) createService() (*v1.Service, error) { | |||
| 	return service, nil | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) 	updateService(newService *v1.Service) error { | ||||
| func (c *Cluster) updateService(newService *v1.Service) error { | ||||
| 	if c.Service == nil { | ||||
| 		return fmt.Errorf("There is no Service in the cluster") | ||||
| 	} | ||||
|  | @ -278,7 +278,7 @@ func (c *Cluster) applySecrets() error { | |||
| 			if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name { | ||||
| 				secretUsername = constants.SuperuserKeyName | ||||
| 				userMap = c.systemUsers | ||||
| 			} else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name  { | ||||
| 			} else if secretUsername == c.systemUsers[constants.ReplicationUserKeyName].Name { | ||||
| 				secretUsername = constants.ReplicationUserKeyName | ||||
| 				userMap = c.systemUsers | ||||
| 			} else { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue