knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/test/upgrade/testing_operations_test.go (about)

     1  /*
     2  Copyright 2020 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package upgrade_test
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"go.uber.org/zap"
    24  	"go.uber.org/zap/zapcore"
    25  
    26  	"knative.dev/pkg/test/upgrade"
    27  )
    28  
    29  const (
    30  	failureTestingMessage = "This error is expected to be seen. Upgrade suite should fail."
    31  )
    32  
    33  func newConfig(t *testing.T) (upgrade.Configuration, fmt.Stringer) {
    34  	buf := threadSafeBuffer{}
    35  	cfg := zap.NewDevelopmentConfig()
    36  	cfg.EncoderConfig.TimeKey = ""
    37  	cfg.EncoderConfig.CallerKey = ""
    38  	syncedBuf := zapcore.AddSync(&buf)
    39  	logConfig := upgrade.LogConfig{
    40  		Options: []zap.Option{
    41  			zap.WrapCore(func(core zapcore.Core) zapcore.Core {
    42  				return zapcore.NewCore(
    43  					zapcore.NewConsoleEncoder(cfg.EncoderConfig),
    44  					zapcore.NewMultiWriteSyncer(syncedBuf), cfg.Level)
    45  			}),
    46  			zap.ErrorOutput(syncedBuf),
    47  		},
    48  	}
    49  	c := upgrade.Configuration{
    50  		T:         t,
    51  		LogConfig: logConfig,
    52  	}
    53  	return c, &buf
    54  }
    55  
    56  func createSteps(s upgrade.Suite) []*step {
    57  	continualTestsGeneralized := generalizeOpsFromBg(s.Tests.Continual)
    58  	return []*step{{
    59  		messages: messageFormatters.baseInstall,
    60  		ops:      generalizeOps(s.Installations.Base),
    61  		updateSuite: func(ops operations, s *upgrade.Suite) {
    62  			s.Installations.Base = ops.asOperations()
    63  		},
    64  	}, {
    65  		messages: messageFormatters.preUpgrade,
    66  		ops:      generalizeOps(s.Tests.PreUpgrade),
    67  		updateSuite: func(ops operations, s *upgrade.Suite) {
    68  			s.Tests.PreUpgrade = ops.asOperations()
    69  		},
    70  	}, {
    71  		messages: messageFormatters.startContinual,
    72  		ops:      continualTestsGeneralized,
    73  		updateSuite: func(ops operations, s *upgrade.Suite) {
    74  			s.Tests.Continual = ops.asBackgroundOperation()
    75  		},
    76  	}, {
    77  		messages: messageFormatters.upgrade,
    78  		ops:      generalizeOps(s.Installations.UpgradeWith),
    79  		updateSuite: func(ops operations, s *upgrade.Suite) {
    80  			s.Installations.UpgradeWith = ops.asOperations()
    81  		},
    82  	}, {
    83  		messages: messageFormatters.postUpgrade,
    84  		ops:      generalizeOps(s.Tests.PostUpgrade),
    85  		updateSuite: func(ops operations, s *upgrade.Suite) {
    86  			s.Tests.PostUpgrade = ops.asOperations()
    87  		},
    88  	}, {
    89  		messages: messageFormatters.downgrade,
    90  		ops:      generalizeOps(s.Installations.DowngradeWith),
    91  		updateSuite: func(ops operations, s *upgrade.Suite) {
    92  			s.Installations.DowngradeWith = ops.asOperations()
    93  		},
    94  	}, {
    95  		messages: messageFormatters.postDowngrade,
    96  		ops:      generalizeOps(s.Tests.PostDowngrade),
    97  		updateSuite: func(ops operations, s *upgrade.Suite) {
    98  			s.Tests.PostDowngrade = ops.asOperations()
    99  		},
   100  	}}
   101  }
   102  
   103  func expectedTexts(s upgrade.Suite, fp failurePoint) texts {
   104  	steps := createSteps(s)
   105  	tt := texts{elms: nil}
   106  	for i, st := range steps {
   107  		stepIdx := i + 1
   108  		if st.ops.length() == 0 {
   109  			tt.append(st.skipped(stepIdx))
   110  		} else {
   111  			tt.append(st.starting(stepIdx, st.ops.length()))
   112  			for j, op := range st.ops.ops {
   113  				elemIdx := j + 1
   114  				tt.append(st.element(stepIdx, elemIdx, op.Name()))
   115  				if fp.step == stepIdx && fp.element == elemIdx {
   116  					return tt
   117  				}
   118  			}
   119  		}
   120  	}
   121  	return tt
   122  }
   123  
   124  func generalizeOps(ops []upgrade.Operation) operations {
   125  	gen := make([]*operation, len(ops))
   126  	for idx, op := range ops {
   127  		gen[idx] = &operation{op: op}
   128  	}
   129  	return operations{ops: gen}
   130  }
   131  
   132  func generalizeOpsFromBg(ops []upgrade.BackgroundOperation) operations {
   133  	gen := make([]*operation, len(ops))
   134  	for idx, op := range ops {
   135  		gen[idx] = &operation{bg: op}
   136  	}
   137  	return operations{ops: gen}
   138  }
   139  
   140  func createMessages(mf formats) messages {
   141  	return messages{
   142  		skipped: func(args ...interface{}) string {
   143  			empty := ""
   144  			if mf.skipped == empty {
   145  				return empty
   146  			}
   147  			return fmt.Sprintf(mf.skipped, args...)
   148  		},
   149  		starting: func(args ...interface{}) string {
   150  			return fmt.Sprintf(mf.starting, args...)
   151  		},
   152  		element: func(args ...interface{}) string {
   153  			return fmt.Sprintf(mf.element, args...)
   154  		},
   155  	}
   156  }
   157  
   158  func (tt *texts) append(messages ...string) {
   159  	for _, msg := range messages {
   160  		if msg == "" {
   161  			continue
   162  		}
   163  		tt.elms = append(tt.elms, msg)
   164  	}
   165  }
   166  
   167  func completeSuite() upgrade.Suite {
   168  	serving := servingComponent()
   169  	eventing := eventingComponent()
   170  	return upgrade.Suite{
   171  		Tests: upgrade.Tests{
   172  			PreUpgrade: []upgrade.Operation{
   173  				serving.tests.preUpgrade, eventing.tests.preUpgrade,
   174  			},
   175  			PostUpgrade: []upgrade.Operation{
   176  				serving.tests.postUpgrade, eventing.tests.postUpgrade,
   177  			},
   178  			PostDowngrade: []upgrade.Operation{
   179  				serving.tests.postDowngrade, eventing.tests.postDowngrade,
   180  			},
   181  			Continual: []upgrade.BackgroundOperation{
   182  				serving.tests.continual,
   183  			},
   184  		},
   185  		Installations: upgrade.Installations{
   186  			Base: []upgrade.Operation{
   187  				serving.installs.stable, eventing.installs.stable,
   188  			},
   189  			UpgradeWith: []upgrade.Operation{
   190  				serving.installs.head, eventing.installs.head,
   191  			},
   192  			DowngradeWith: []upgrade.Operation{
   193  				serving.installs.stable, eventing.installs.stable,
   194  			},
   195  		},
   196  	}
   197  }
   198  
   199  func completeSuiteExampleWithFailures(fp failurePoint) upgrade.Suite {
   200  	return enrichSuiteWithFailures(completeSuite(), fp)
   201  }
   202  
   203  func emptySuiteExample() upgrade.Suite {
   204  	return upgrade.Suite{
   205  		Tests:         upgrade.Tests{},
   206  		Installations: upgrade.Installations{},
   207  	}
   208  }
   209  
   210  func enrichSuiteWithFailures(suite upgrade.Suite, fp failurePoint) upgrade.Suite {
   211  	steps := createSteps(suite)
   212  	for i, st := range steps {
   213  		for j, op := range st.ops.ops {
   214  			if fp.step == i+1 && fp.element == j+1 {
   215  				op.fail(fp.step == 3)
   216  			}
   217  		}
   218  	}
   219  	return recreateSuite(steps)
   220  }
   221  
   222  func recreateSuite(steps []*step) upgrade.Suite {
   223  	suite := &upgrade.Suite{
   224  		Tests:         upgrade.Tests{},
   225  		Installations: upgrade.Installations{},
   226  	}
   227  	for _, st := range steps {
   228  		st.updateSuite(st.ops, suite)
   229  	}
   230  	return *suite
   231  }
   232  
   233  func (o operation) Name() string {
   234  	if o.op != nil {
   235  		return o.op.Name()
   236  	}
   237  	return o.bg.Name()
   238  }
   239  
   240  func (o *operation) fail(setupFail bool) {
   241  	testName := "FailingOf" + o.Name()
   242  	if o.op != nil {
   243  		prev := o.op
   244  		o.op = upgrade.NewOperation(testName, func(c upgrade.Context) {
   245  			handler := prev.Handler()
   246  			handler(c)
   247  			c.T.Error(failureTestingMessage)
   248  			c.Log.Error(failureTestingMessage)
   249  		})
   250  	} else {
   251  		prev := o.bg
   252  		o.bg = upgrade.NewBackgroundOperation(testName, func(c upgrade.Context) {
   253  			setup := prev.Setup()
   254  			setup(c)
   255  			if setupFail {
   256  				c.T.Error(failureTestingMessage)
   257  				c.Log.Error(failureTestingMessage)
   258  			}
   259  		}, func(bc upgrade.BackgroundContext) {
   260  			upgrade.WaitForStopEvent(bc, upgrade.WaitForStopEventConfiguration{
   261  				Name: testName,
   262  				OnStop: func() {
   263  					if !setupFail {
   264  						bc.T.Error(failureTestingMessage)
   265  						bc.Log.Error(failureTestingMessage)
   266  					}
   267  				},
   268  				OnWait: func(bc upgrade.BackgroundContext, self upgrade.WaitForStopEventConfiguration) {
   269  					bc.Log.Debugf("%s - probing functionality...", self.Name)
   270  				},
   271  				WaitTime: shortWait,
   272  			})
   273  		})
   274  	}
   275  }
   276  
   277  func (o operations) length() int {
   278  	return len(o.ops)
   279  }
   280  
   281  func (o operations) asOperations() []upgrade.Operation {
   282  	ops := make([]upgrade.Operation, o.length())
   283  	for i, op := range o.ops {
   284  		ops[i] = op.op
   285  	}
   286  	return ops
   287  }
   288  
   289  func (o operations) asBackgroundOperation() []upgrade.BackgroundOperation {
   290  	ops := make([]upgrade.BackgroundOperation, o.length())
   291  	for i, op := range o.ops {
   292  		ops[i] = op.bg
   293  	}
   294  	return ops
   295  }