...

Source file src/go.etcd.io/etcd/server/v3/etcdserver/api/membership/downgrade_test.go

Documentation: go.etcd.io/etcd/server/v3/etcdserver/api/membership

     1  // Copyright 2020 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package membership
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strconv"
    25  	"testing"
    26  
    27  	"github.com/coreos/go-semver/semver"
    28  	"go.etcd.io/etcd/api/v3/version"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  func TestMustDetectDowngrade(t *testing.T) {
    33  	lv := semver.Must(semver.NewVersion(version.Version))
    34  	lv = &semver.Version{Major: lv.Major, Minor: lv.Minor}
    35  	oneMinorHigher := &semver.Version{Major: lv.Major, Minor: lv.Minor + 1}
    36  	oneMinorLower := &semver.Version{Major: lv.Major, Minor: lv.Minor - 1}
    37  	downgradeEnabledHigherVersion := &DowngradeInfo{Enabled: true, TargetVersion: oneMinorHigher.String()}
    38  	downgradeEnabledEqualVersion := &DowngradeInfo{Enabled: true, TargetVersion: lv.String()}
    39  	downgradeEnabledLowerVersion := &DowngradeInfo{Enabled: true, TargetVersion: oneMinorLower.String()}
    40  	downgradeDisabled := &DowngradeInfo{Enabled: false}
    41  
    42  	tests := []struct {
    43  		name           string
    44  		clusterVersion *semver.Version
    45  		downgrade      *DowngradeInfo
    46  		success        bool
    47  		message        string
    48  	}{
    49  		{
    50  			"Succeeded when downgrade is disabled and cluster version is nil",
    51  			nil,
    52  			downgradeDisabled,
    53  			true,
    54  			"",
    55  		},
    56  		{
    57  			"Succeeded when downgrade is disabled and cluster version is one minor lower",
    58  			oneMinorLower,
    59  			downgradeDisabled,
    60  			true,
    61  			"",
    62  		},
    63  		{
    64  			"Succeeded when downgrade is disabled and cluster version is server version",
    65  			lv,
    66  			downgradeDisabled,
    67  			true,
    68  			"",
    69  		},
    70  		{
    71  			"Failed when downgrade is disabled and server version is lower than determined cluster version ",
    72  			oneMinorHigher,
    73  			downgradeDisabled,
    74  			false,
    75  			"invalid downgrade; server version is lower than determined cluster version",
    76  		},
    77  		{
    78  			"Succeeded when downgrade is enabled and cluster version is nil",
    79  			nil,
    80  			downgradeEnabledEqualVersion,
    81  			true,
    82  			"",
    83  		},
    84  		{
    85  			"Failed when downgrade is enabled and server version is target version",
    86  			lv,
    87  			downgradeEnabledEqualVersion,
    88  			true,
    89  			"cluster is downgrading to target version",
    90  		},
    91  		{
    92  			"Succeeded when downgrade to lower version and server version is cluster version ",
    93  			lv,
    94  			downgradeEnabledLowerVersion,
    95  			false,
    96  			"invalid downgrade; server version is not allowed to join when downgrade is enabled",
    97  		},
    98  		{
    99  			"Failed when downgrade is enabled and local version is out of range and cluster version is nil",
   100  			nil,
   101  			downgradeEnabledHigherVersion,
   102  			false,
   103  			"invalid downgrade; server version is not allowed to join when downgrade is enabled",
   104  		},
   105  
   106  		{
   107  			"Failed when downgrade is enabled and local version is out of range",
   108  			lv,
   109  			downgradeEnabledHigherVersion,
   110  			false,
   111  			"invalid downgrade; server version is not allowed to join when downgrade is enabled",
   112  		},
   113  	}
   114  
   115  	if os.Getenv("DETECT_DOWNGRADE") != "" {
   116  		i := os.Getenv("DETECT_DOWNGRADE")
   117  		iint, _ := strconv.Atoi(i)
   118  		logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-must-detect-downgrade-%v", iint))
   119  
   120  		lcfg := zap.NewProductionConfig()
   121  		lcfg.OutputPaths = []string{logPath}
   122  		lcfg.ErrorOutputPaths = []string{logPath}
   123  		lg, _ := lcfg.Build()
   124  
   125  		mustDetectDowngrade(lg, tests[iint].clusterVersion, tests[iint].downgrade)
   126  		return
   127  	}
   128  
   129  	for i, tt := range tests {
   130  		t.Run(tt.name, func(t *testing.T) {
   131  			logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-must-detect-downgrade-%d", i))
   132  			t.Log(logPath)
   133  			defer os.RemoveAll(logPath)
   134  
   135  			cmd := exec.Command(os.Args[0], "-test.run=TestMustDetectDowngrade")
   136  			cmd.Env = append(os.Environ(), fmt.Sprintf("DETECT_DOWNGRADE=%d", i))
   137  			if err := cmd.Start(); err != nil {
   138  				t.Fatal(err)
   139  			}
   140  
   141  			errCmd := cmd.Wait()
   142  
   143  			data, err := ioutil.ReadFile(logPath)
   144  			if err == nil {
   145  				if !bytes.Contains(data, []byte(tt.message)) {
   146  					t.Errorf("Expected to find %v in log", tt.message)
   147  				}
   148  			} else {
   149  				t.Fatal(err)
   150  			}
   151  
   152  			if !tt.success {
   153  				e, ok := errCmd.(*exec.ExitError)
   154  				if !ok || e.Success() {
   155  					t.Errorf("Expected exit with status 1; Got %v", err)
   156  				}
   157  			}
   158  
   159  			if tt.success && errCmd != nil {
   160  				t.Errorf("Expected not failure; Got %v", errCmd)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestIsValidDowngrade(t *testing.T) {
   167  	tests := []struct {
   168  		name    string
   169  		verFrom string
   170  		verTo   string
   171  		result  bool
   172  	}{
   173  		{
   174  			"Valid downgrade",
   175  			"3.5.0",
   176  			"3.4.0",
   177  			true,
   178  		},
   179  		{
   180  			"Invalid downgrade",
   181  			"3.5.2",
   182  			"3.3.0",
   183  			false,
   184  		},
   185  	}
   186  	for _, tt := range tests {
   187  		t.Run(tt.name, func(t *testing.T) {
   188  			res := isValidDowngrade(
   189  				semver.Must(semver.NewVersion(tt.verFrom)), semver.Must(semver.NewVersion(tt.verTo)))
   190  			if res != tt.result {
   191  				t.Errorf("Expected downgrade valid is %v; Got %v", tt.result, res)
   192  			}
   193  		})
   194  	}
   195  }
   196  

View as plain text