More tests and more nice diff.
This commit is contained in:
parent
e40abdb249
commit
1f3730b2b4
|
|
@ -12,6 +12,10 @@ Docker.
|
||||||
Docker
|
Docker
|
||||||
Go
|
Go
|
||||||
|
|
||||||
|
# Notice
|
||||||
|
|
||||||
|
The `manifest` folder in e2e tests folder is not commited to git, it comes from `/manifests`
|
||||||
|
|
||||||
## Build test runner
|
## Build test runner
|
||||||
|
|
||||||
In the directory of the cloned Postgres Operator repository change to the e2e
|
In the directory of the cloned Postgres Operator repository change to the e2e
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,14 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
|
|
||||||
k8s.wait_for_operator_pod_start()
|
k8s.wait_for_operator_pod_start()
|
||||||
|
|
||||||
|
# make sure we start a new operator on every new run,
|
||||||
|
# this tackles the problem when kind is reused
|
||||||
|
# and the Docker image is infact changed (dirty one)
|
||||||
|
|
||||||
|
# patch resync period, this can catch some problems with hanging e2e tests
|
||||||
|
# k8s.update_config({"data": {"resync_period":"30s"}},step="TestSuite setup")
|
||||||
|
k8s.update_config({}, step="TestSuite Startup")
|
||||||
|
|
||||||
actual_operator_image = k8s.api.core_v1.list_namespaced_pod(
|
actual_operator_image = k8s.api.core_v1.list_namespaced_pod(
|
||||||
'default', label_selector='name=postgres-operator').items[0].spec.containers[0].image
|
'default', label_selector='name=postgres-operator').items[0].spec.containers[0].image
|
||||||
print("Tested operator image: {}".format(actual_operator_image)) # shows up after tests finish
|
print("Tested operator image: {}".format(actual_operator_image)) # shows up after tests finish
|
||||||
|
|
@ -454,8 +462,6 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
Lower resource limits below configured minimum and let operator fix it
|
Lower resource limits below configured minimum and let operator fix it
|
||||||
'''
|
'''
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
cluster_label = 'application=spilo,cluster-name=acid-minimal-cluster'
|
|
||||||
_, failover_targets = k8s.get_pg_nodes(cluster_label)
|
|
||||||
|
|
||||||
# configure minimum boundaries for CPU and memory limits
|
# configure minimum boundaries for CPU and memory limits
|
||||||
minCPULimit = '503m'
|
minCPULimit = '503m'
|
||||||
|
|
@ -545,6 +551,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.assertTrue(len(failover_targets)>0, "No failover targets available")
|
||||||
for failover_target in failover_targets:
|
for failover_target in failover_targets:
|
||||||
k8s.api.core_v1.patch_node(failover_target, patch_readiness_label)
|
k8s.api.core_v1.patch_node(failover_target, patch_readiness_label)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
|
acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
|
||||||
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||||
"github.com/zalando/postgres-operator/pkg/spec"
|
"github.com/zalando/postgres-operator/pkg/spec"
|
||||||
"github.com/zalando/postgres-operator/pkg/util"
|
"github.com/zalando/postgres-operator/pkg/util"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/constants"
|
"github.com/zalando/postgres-operator/pkg/util/constants"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
||||||
|
"github.com/zalando/postgres-operator/pkg/util/nicediff"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/retryutil"
|
"github.com/zalando/postgres-operator/pkg/util/retryutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -169,6 +171,20 @@ func (c *Cluster) logPDBChanges(old, new *policybeta1.PodDisruptionBudget, isUpd
|
||||||
c.logger.Debugf("diff\n%s\n", util.PrettyDiff(old.Spec, new.Spec))
|
c.logger.Debugf("diff\n%s\n", util.PrettyDiff(old.Spec, new.Spec))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logNiceDiff(log *logrus.Entry, old, new interface{}) {
|
||||||
|
o, erro := json.MarshalIndent(old, "", " ")
|
||||||
|
n, errn := json.MarshalIndent(new, "", " ")
|
||||||
|
|
||||||
|
if erro != nil || errn != nil {
|
||||||
|
panic("could not marschal API objects, should not happen")
|
||||||
|
}
|
||||||
|
|
||||||
|
nice := nicediff.Diff(string(o), string(n), true)
|
||||||
|
for _, s := range strings.Split(nice, "\n") {
|
||||||
|
log.Debugf(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cluster) logStatefulSetChanges(old, new *appsv1.StatefulSet, isUpdate bool, reasons []string) {
|
func (c *Cluster) logStatefulSetChanges(old, new *appsv1.StatefulSet, isUpdate bool, reasons []string) {
|
||||||
if isUpdate {
|
if isUpdate {
|
||||||
c.logger.Infof("statefulset %q has been changed", util.NameFromMeta(old.ObjectMeta))
|
c.logger.Infof("statefulset %q has been changed", util.NameFromMeta(old.ObjectMeta))
|
||||||
|
|
@ -180,7 +196,9 @@ func (c *Cluster) logStatefulSetChanges(old, new *appsv1.StatefulSet, isUpdate b
|
||||||
if !reflect.DeepEqual(old.Annotations, new.Annotations) {
|
if !reflect.DeepEqual(old.Annotations, new.Annotations) {
|
||||||
c.logger.Debugf("metadata.annotation diff\n%s\n", util.PrettyDiff(old.Annotations, new.Annotations))
|
c.logger.Debugf("metadata.annotation diff\n%s\n", util.PrettyDiff(old.Annotations, new.Annotations))
|
||||||
}
|
}
|
||||||
c.logger.Debugf("spec diff between old and new statefulsets: \n%s\n", util.PrettyDiff(old.Spec, new.Spec))
|
|
||||||
|
c.logger.Debugf("Statefulset spec is different")
|
||||||
|
logNiceDiff(c.logger, old.Spec, new.Spec)
|
||||||
|
|
||||||
if len(reasons) > 0 {
|
if len(reasons) > 0 {
|
||||||
for _, reason := range reasons {
|
for _, reason := range reasons {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -180,3 +180,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")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue