mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-05 01:13:54 +02:00
merge with master
This commit is contained in:
+29
-30
@@ -143,14 +143,13 @@ type Config struct {
|
||||
LogicalBackup
|
||||
ConnectionPooler
|
||||
|
||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
||||
KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"`
|
||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-12:1.6-p3"`
|
||||
// deprecated in favour of SidecarContainers
|
||||
SidecarImages map[string]string `name:"sidecar_docker_images"`
|
||||
SidecarContainers []v1.Container `name:"sidecars"`
|
||||
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
|
||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
||||
KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"`
|
||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-12:1.6-p3"`
|
||||
SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers
|
||||
SidecarContainers []v1.Container `name:"sidecars"`
|
||||
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
|
||||
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
||||
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
||||
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
||||
@@ -170,6 +169,8 @@ type Config struct {
|
||||
EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"`
|
||||
TeamAdminRole string `name:"team_admin_role" default:"admin"`
|
||||
EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"`
|
||||
EnablePostgresTeamCRD bool `name:"enable_postgres_team_crd" default:"false"`
|
||||
EnablePostgresTeamCRDSuperusers bool `name:"enable_postgres_team_crd_superusers" default:"false"`
|
||||
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
|
||||
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
|
||||
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
|
||||
@@ -177,32 +178,30 @@ type Config struct {
|
||||
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
|
||||
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
|
||||
StorageResizeMode string `name:"storage_resize_mode" default:"ebs"`
|
||||
// ExternalTrafficPolicy for load balancer
|
||||
ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"`
|
||||
// deprecated and kept for backward compatibility
|
||||
EnableLoadBalancer *bool `name:"enable_load_balancer"`
|
||||
MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"`
|
||||
ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"`
|
||||
PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"`
|
||||
EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"`
|
||||
EnableInitContainers *bool `name:"enable_init_containers" default:"true"`
|
||||
EnableSidecars *bool `name:"enable_sidecars" default:"true"`
|
||||
Workers uint32 `name:"workers" default:"8"`
|
||||
APIPort int `name:"api_port" default:"8080"`
|
||||
RingLogLines int `name:"ring_log_lines" default:"100"`
|
||||
ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"`
|
||||
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
|
||||
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
|
||||
ProtectedRoles []string `name:"protected_role_names" default:"admin"`
|
||||
PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""`
|
||||
SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"`
|
||||
EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"`
|
||||
EnableLoadBalancer *bool `name:"enable_load_balancer"` // deprecated and kept for backward compatibility
|
||||
ExternalTrafficPolicy string `name:"external_traffic_policy" default:"Cluster"`
|
||||
MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"`
|
||||
ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"`
|
||||
PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"`
|
||||
EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"`
|
||||
EnableInitContainers *bool `name:"enable_init_containers" default:"true"`
|
||||
EnableSidecars *bool `name:"enable_sidecars" default:"true"`
|
||||
Workers uint32 `name:"workers" default:"8"`
|
||||
APIPort int `name:"api_port" default:"8080"`
|
||||
RingLogLines int `name:"ring_log_lines" default:"100"`
|
||||
ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"`
|
||||
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
|
||||
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
|
||||
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
|
||||
ProtectedRoles []string `name:"protected_role_names" default:"admin"`
|
||||
PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""`
|
||||
SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"`
|
||||
EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"`
|
||||
}
|
||||
|
||||
// MustMarshal marshals the config or panics
|
||||
func (c Config) MustMarshal() string {
|
||||
b, err := json.MarshalIndent(c, "", "\t")
|
||||
b, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package diff implements a linewise diff algorithm.
|
||||
package nicediff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Chunk represents a piece of the diff. A chunk will not have both added and
|
||||
// deleted lines. Equal lines are always after any added or deleted lines.
|
||||
// A Chunk may or may not have any lines in it, especially for the first or last
|
||||
// chunk in a computation.
|
||||
type Chunk struct {
|
||||
Added []string
|
||||
Deleted []string
|
||||
Equal []string
|
||||
}
|
||||
|
||||
func (c *Chunk) empty() bool {
|
||||
return len(c.Added) == 0 && len(c.Deleted) == 0 && len(c.Equal) == 0
|
||||
}
|
||||
|
||||
// Diff returns a string containing a line-by-line unified diff of the linewise
|
||||
// changes required to make A into B. Each line is prefixed with '+', '-', or
|
||||
// ' ' to indicate if it should be added, removed, or is correct respectively.
|
||||
func Diff(A, B string, skipEqual bool) string {
|
||||
aLines := strings.Split(A, "\n")
|
||||
bLines := strings.Split(B, "\n")
|
||||
return Render(DiffChunks(aLines, bLines), skipEqual)
|
||||
}
|
||||
|
||||
// Render renders the slice of chunks into a representation that prefixes
|
||||
// the lines with '+', '-', or ' ' depending on whether the line was added,
|
||||
// removed, or equal (respectively).
|
||||
func Render(chunks []Chunk, skipEqual bool) string {
|
||||
buf := new(strings.Builder)
|
||||
for _, c := range chunks {
|
||||
for _, line := range c.Added {
|
||||
fmt.Fprintf(buf, "+%s\n", line)
|
||||
}
|
||||
for _, line := range c.Deleted {
|
||||
fmt.Fprintf(buf, "-%s\n", line)
|
||||
}
|
||||
if !skipEqual {
|
||||
for _, line := range c.Equal {
|
||||
fmt.Fprintf(buf, " %s\n", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.TrimRight(buf.String(), "\n")
|
||||
}
|
||||
|
||||
// DiffChunks uses an O(D(N+M)) shortest-edit-script algorithm
|
||||
// to compute the edits required from A to B and returns the
|
||||
// edit chunks.
|
||||
func DiffChunks(a, b []string) []Chunk {
|
||||
// algorithm: http://www.xmailserver.org/diff2.pdf
|
||||
|
||||
// We'll need these quantities a lot.
|
||||
alen, blen := len(a), len(b) // M, N
|
||||
|
||||
// At most, it will require len(a) deletions and len(b) additions
|
||||
// to transform a into b.
|
||||
maxPath := alen + blen // MAX
|
||||
if maxPath == 0 {
|
||||
// degenerate case: two empty lists are the same
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store the endpoint of the path for diagonals.
|
||||
// We store only the a index, because the b index on any diagonal
|
||||
// (which we know during the loop below) is aidx-diag.
|
||||
// endpoint[maxPath] represents the 0 diagonal.
|
||||
//
|
||||
// Stated differently:
|
||||
// endpoint[d] contains the aidx of a furthest reaching path in diagonal d
|
||||
endpoint := make([]int, 2*maxPath+1) // V
|
||||
|
||||
saved := make([][]int, 0, 8) // Vs
|
||||
save := func() {
|
||||
dup := make([]int, len(endpoint))
|
||||
copy(dup, endpoint)
|
||||
saved = append(saved, dup)
|
||||
}
|
||||
|
||||
var editDistance int // D
|
||||
dLoop:
|
||||
for editDistance = 0; editDistance <= maxPath; editDistance++ {
|
||||
// The 0 diag(onal) represents equality of a and b. Each diagonal to
|
||||
// the left is numbered one lower, to the right is one higher, from
|
||||
// -alen to +blen. Negative diagonals favor differences from a,
|
||||
// positive diagonals favor differences from b. The edit distance to a
|
||||
// diagonal d cannot be shorter than d itself.
|
||||
//
|
||||
// The iterations of this loop cover either odds or evens, but not both,
|
||||
// If odd indices are inputs, even indices are outputs and vice versa.
|
||||
for diag := -editDistance; diag <= editDistance; diag += 2 { // k
|
||||
var aidx int // x
|
||||
switch {
|
||||
case diag == -editDistance:
|
||||
// This is a new diagonal; copy from previous iter
|
||||
aidx = endpoint[maxPath-editDistance+1] + 0
|
||||
case diag == editDistance:
|
||||
// This is a new diagonal; copy from previous iter
|
||||
aidx = endpoint[maxPath+editDistance-1] + 1
|
||||
case endpoint[maxPath+diag+1] > endpoint[maxPath+diag-1]:
|
||||
// diagonal d+1 was farther along, so use that
|
||||
aidx = endpoint[maxPath+diag+1] + 0
|
||||
default:
|
||||
// diagonal d-1 was farther (or the same), so use that
|
||||
aidx = endpoint[maxPath+diag-1] + 1
|
||||
}
|
||||
// On diagonal d, we can compute bidx from aidx.
|
||||
bidx := aidx - diag // y
|
||||
// See how far we can go on this diagonal before we find a difference.
|
||||
for aidx < alen && bidx < blen && a[aidx] == b[bidx] {
|
||||
aidx++
|
||||
bidx++
|
||||
}
|
||||
// Store the end of the current edit chain.
|
||||
endpoint[maxPath+diag] = aidx
|
||||
// If we've found the end of both inputs, we're done!
|
||||
if aidx >= alen && bidx >= blen {
|
||||
save() // save the final path
|
||||
break dLoop
|
||||
}
|
||||
}
|
||||
save() // save the current path
|
||||
}
|
||||
if editDistance == 0 {
|
||||
return nil
|
||||
}
|
||||
chunks := make([]Chunk, editDistance+1)
|
||||
|
||||
x, y := alen, blen
|
||||
for d := editDistance; d > 0; d-- {
|
||||
endpoint := saved[d]
|
||||
diag := x - y
|
||||
insert := diag == -d || (diag != d && endpoint[maxPath+diag-1] < endpoint[maxPath+diag+1])
|
||||
|
||||
x1 := endpoint[maxPath+diag]
|
||||
var x0, xM, kk int
|
||||
if insert {
|
||||
kk = diag + 1
|
||||
x0 = endpoint[maxPath+kk]
|
||||
xM = x0
|
||||
} else {
|
||||
kk = diag - 1
|
||||
x0 = endpoint[maxPath+kk]
|
||||
xM = x0 + 1
|
||||
}
|
||||
y0 := x0 - kk
|
||||
|
||||
var c Chunk
|
||||
if insert {
|
||||
c.Added = b[y0:][:1]
|
||||
} else {
|
||||
c.Deleted = a[x0:][:1]
|
||||
}
|
||||
if xM < x1 {
|
||||
c.Equal = a[xM:][:x1-xM]
|
||||
}
|
||||
|
||||
x, y = x0, y0
|
||||
chunks[d] = c
|
||||
}
|
||||
if x > 0 {
|
||||
chunks[0].Equal = a[:x]
|
||||
}
|
||||
if chunks[0].empty() {
|
||||
chunks = chunks[1:]
|
||||
}
|
||||
if len(chunks) == 0 {
|
||||
return nil
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -134,6 +136,21 @@ func PrettyDiff(a, b interface{}) string {
|
||||
return strings.Join(Diff(a, b), "\n")
|
||||
}
|
||||
|
||||
// Compare two string slices while ignoring the order of elements
|
||||
func IsEqualIgnoreOrder(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
a_copy := make([]string, len(a))
|
||||
b_copy := make([]string, len(b))
|
||||
copy(a_copy, a)
|
||||
copy(b_copy, b)
|
||||
sort.Strings(a_copy)
|
||||
sort.Strings(b_copy)
|
||||
|
||||
return reflect.DeepEqual(a_copy, b_copy)
|
||||
}
|
||||
|
||||
// SubstractStringSlices finds elements in a that are not in b and return them as a result slice.
|
||||
func SubstractStringSlices(a []string, b []string) (result []string, equal bool) {
|
||||
// Slices are assumed to contain unique elements only
|
||||
@@ -176,6 +193,20 @@ func FindNamedStringSubmatch(r *regexp.Regexp, s string) map[string]string {
|
||||
return res
|
||||
}
|
||||
|
||||
// SliceContains
|
||||
func SliceContains(slice interface{}, item interface{}) bool {
|
||||
s := reflect.ValueOf(slice)
|
||||
if s.Kind() != reflect.Slice {
|
||||
panic("Invalid data-type")
|
||||
}
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
if s.Index(i).Interface() == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MapContains returns true if and only if haystack contains all the keys from the needle with matching corresponding values
|
||||
func MapContains(haystack, needle map[string]string) bool {
|
||||
if len(haystack) < len(needle) {
|
||||
|
||||
@@ -43,6 +43,17 @@ var prettyDiffTest = []struct {
|
||||
{[]int{1, 2, 3, 4}, []int{1, 2, 3, 4}, ""},
|
||||
}
|
||||
|
||||
var isEqualIgnoreOrderTest = []struct {
|
||||
inA []string
|
||||
inB []string
|
||||
outEqual bool
|
||||
}{
|
||||
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true},
|
||||
{[]string{"a", "b", "c"}, []string{"a", "c", "b"}, true},
|
||||
{[]string{"a", "b"}, []string{"a", "c", "b"}, false},
|
||||
{[]string{"a", "b", "c"}, []string{"a", "d", "c"}, false},
|
||||
}
|
||||
|
||||
var substractTest = []struct {
|
||||
inA []string
|
||||
inB []string
|
||||
@@ -53,6 +64,16 @@ var substractTest = []struct {
|
||||
{[]string{"a", "b", "c", "d"}, []string{"a", "bb", "c", "d"}, []string{"b"}, false},
|
||||
}
|
||||
|
||||
var sliceContaintsTest = []struct {
|
||||
slice []string
|
||||
item string
|
||||
out bool
|
||||
}{
|
||||
{[]string{"a", "b", "c"}, "a", true},
|
||||
{[]string{"a", "b", "c"}, "d", false},
|
||||
{[]string{}, "d", false},
|
||||
}
|
||||
|
||||
var mapContaintsTest = []struct {
|
||||
inA map[string]string
|
||||
inB map[string]string
|
||||
@@ -136,6 +157,15 @@ func TestPrettyDiff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEqualIgnoreOrder(t *testing.T) {
|
||||
for _, tt := range isEqualIgnoreOrderTest {
|
||||
actualEqual := IsEqualIgnoreOrder(tt.inA, tt.inB)
|
||||
if actualEqual != tt.outEqual {
|
||||
t.Errorf("IsEqualIgnoreOrder expected: %t, got: %t", tt.outEqual, actualEqual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubstractSlices(t *testing.T) {
|
||||
for _, tt := range substractTest {
|
||||
actualRes, actualEqual := SubstractStringSlices(tt.inA, tt.inB)
|
||||
@@ -160,6 +190,15 @@ func TestFindNamedStringSubmatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSliceContains(t *testing.T) {
|
||||
for _, tt := range sliceContaintsTest {
|
||||
res := SliceContains(tt.slice, tt.item)
|
||||
if res != tt.out {
|
||||
t.Errorf("SliceContains expected: %#v, got: %#v", tt.out, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapContains(t *testing.T) {
|
||||
for _, tt := range mapContaintsTest {
|
||||
res := MapContains(tt.inA, tt.inB)
|
||||
@@ -180,3 +219,13 @@ func TestIsSmallerQuantity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestNiceDiff(t *testing.T) {
|
||||
o := "a\nb\nc\n"
|
||||
n := "b\nd\n"
|
||||
d := nicediff.Diff(o, n, true)
|
||||
t.Log(d)
|
||||
// t.Errorf("Lets see output")
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user