github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/staged_manager_test.go (about)

     1  package process_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/kyma-project/kyma-environment-broker/internal/process"
    11  
    12  	"github.com/kyma-project/kyma-environment-broker/internal/ptr"
    13  
    14  	"github.com/kyma-project/kyma-environment-broker/internal/broker"
    15  	"github.com/kyma-project/kyma-environment-broker/internal/fixture"
    16  	"github.com/pivotal-cf/brokerapi/v8/domain"
    17  	"k8s.io/apimachinery/pkg/util/wait"
    18  
    19  	"github.com/kyma-project/kyma-environment-broker/internal"
    20  	"github.com/kyma-project/kyma-environment-broker/internal/event"
    21  	"github.com/kyma-project/kyma-environment-broker/internal/storage"
    22  	"github.com/sirupsen/logrus"
    23  	"github.com/stretchr/testify/assert"
    24  )
    25  
    26  const (
    27  	globalAccountID = "80ac17bd-33e8-4ffa-8d56-1d5367755723"
    28  	subAccountID    = "12df5747-3efb-4df6-ad6f-4414bb661ce3"
    29  )
    30  
    31  func TestHappyPath(t *testing.T) {
    32  	// given
    33  	const opID = "op-0001234"
    34  	operation := FixOperation("op-0001234")
    35  	mgr, operationStorage, eventCollector := SetupStagedManager(operation)
    36  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil)
    37  	mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil)
    38  	mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil)
    39  	mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil)
    40  
    41  	// when
    42  	mgr.Execute(operation.ID)
    43  
    44  	// then
    45  	eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third", "first-2"})
    46  	op, _ := operationStorage.GetOperationByID(operation.ID)
    47  	assert.True(t, op.IsStageFinished("stage-1"))
    48  	assert.True(t, op.IsStageFinished("stage-2"))
    49  }
    50  
    51  func TestHappyPathWithStepCondition(t *testing.T) {
    52  	// given
    53  	const opID = "op-0001234"
    54  	operation := FixOperation("op-0001234")
    55  	mgr, operationStorage, eventCollector := SetupStagedManager(operation)
    56  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil)
    57  	mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, func(_ internal.Operation) bool {
    58  		return false
    59  	})
    60  	mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, func(_ internal.Operation) bool {
    61  		return true
    62  	})
    63  	mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil)
    64  
    65  	// when
    66  	mgr.Execute(operation.ID)
    67  
    68  	// then
    69  	eventCollector.AssertProcessedSteps(t, []string{"first", "third", "first-2"})
    70  	op, _ := operationStorage.GetOperationByID(operation.ID)
    71  	assert.True(t, op.IsStageFinished("stage-1"))
    72  	assert.True(t, op.IsStageFinished("stage-2"))
    73  }
    74  
    75  func TestWithRetry(t *testing.T) {
    76  	// given
    77  	const opID = "op-0001234"
    78  	operation := FixOperation("op-0001234")
    79  	mgr, operationStorage, eventCollector := SetupStagedManager(operation)
    80  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil)
    81  	mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil)
    82  	mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil)
    83  	mgr.AddStep("stage-2", &onceRetryingStep{name: "first-2", eventPublisher: eventCollector}, nil)
    84  	mgr.AddStep("stage-2", &testingStep{name: "second-2", eventPublisher: eventCollector}, nil)
    85  
    86  	// when
    87  	retry, _ := mgr.Execute(operation.ID)
    88  
    89  	// then
    90  	assert.Zero(t, retry)
    91  	eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third", "first-2", "first-2", "second-2"})
    92  	op, _ := operationStorage.GetOperationByID(operation.ID)
    93  	assert.True(t, op.IsStageFinished("stage-1"))
    94  	assert.True(t, op.IsStageFinished("stage-2"))
    95  }
    96  
    97  func TestWithPanic(t *testing.T) {
    98  	// given
    99  	const opID = "op-0001234"
   100  	operation := FixOperation("op-0001234")
   101  	mgr, operationStorage, eventCollector := SetupStagedManager(operation)
   102  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil)
   103  	mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil)
   104  	mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil)
   105  	mgr.AddStep("stage-2", &panicStep{name: "first-2-panic", eventPublisher: eventCollector}, nil)
   106  	mgr.AddStep("stage-2", &testingStep{name: "second-2-after-panic", eventPublisher: eventCollector}, nil)
   107  
   108  	// when
   109  	mgr.Execute(operation.ID)
   110  
   111  	// then
   112  	eventCollector.AssertProcessedSteps(t, []string{"first", "second", "third"})
   113  	op, _ := operationStorage.GetOperationByID(operation.ID)
   114  	assert.Equal(t, op.State, domain.Failed)
   115  	assert.True(t, op.IsStageFinished("stage-1"))
   116  	assert.False(t, op.IsStageFinished("stage-2"))
   117  }
   118  
   119  func TestSkipFinishedStage(t *testing.T) {
   120  	// given
   121  	operation := FixOperation("op-0001234")
   122  	operation.FinishStage("stage-1")
   123  
   124  	mgr, operationStorage, eventCollector := SetupStagedManager(operation)
   125  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: eventCollector}, nil)
   126  	mgr.AddStep("stage-1", &testingStep{name: "second", eventPublisher: eventCollector}, nil)
   127  	mgr.AddStep("stage-1", &testingStep{name: "third", eventPublisher: eventCollector}, nil)
   128  	mgr.AddStep("stage-2", &testingStep{name: "first-2", eventPublisher: eventCollector}, nil)
   129  
   130  	// when
   131  	retry, _ := mgr.Execute(operation.ID)
   132  
   133  	// then
   134  	assert.Zero(t, retry)
   135  	eventCollector.WaitForEvents(t, 1)
   136  	op, _ := operationStorage.GetOperationByID(operation.ID)
   137  	assert.True(t, op.IsStageFinished("stage-1"))
   138  	assert.True(t, op.IsStageFinished("stage-2"))
   139  }
   140  
   141  func SetupStagedManager(op internal.Operation) (*process.StagedManager, storage.Operations, *CollectingEventHandler) {
   142  	memoryStorage := storage.NewMemoryStorage()
   143  	memoryStorage.Operations().InsertOperation(op)
   144  
   145  	eventCollector := &CollectingEventHandler{}
   146  	l := logrus.New()
   147  	l.SetLevel(logrus.DebugLevel)
   148  	mgr := process.NewStagedManager(memoryStorage.Operations(), eventCollector, 3*time.Second, process.StagedManagerConfiguration{MaxStepProcessingTime: time.Second}, l)
   149  	mgr.SpeedUp(100000)
   150  	mgr.DefineStages([]string{"stage-1", "stage-2"})
   151  
   152  	return mgr, memoryStorage.Operations(), eventCollector
   153  }
   154  
   155  type testingStep struct {
   156  	name           string
   157  	eventPublisher event.Publisher
   158  }
   159  
   160  func (s *testingStep) Name() string {
   161  	return s.name
   162  }
   163  func (s *testingStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) {
   164  	logger.Infof("Running")
   165  	s.eventPublisher.Publish(context.Background(), s.name)
   166  	return operation, 0, nil
   167  }
   168  
   169  type onceRetryingStep struct {
   170  	name           string
   171  	processed      bool
   172  	eventPublisher event.Publisher
   173  }
   174  
   175  func (s *onceRetryingStep) Name() string {
   176  	return s.name
   177  }
   178  func (s *onceRetryingStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) {
   179  	s.eventPublisher.Publish(context.Background(), s.name)
   180  	if !s.processed {
   181  		s.processed = true
   182  		return operation, time.Millisecond, nil
   183  	}
   184  	logger.Infof("Running")
   185  	return operation, 0, nil
   186  }
   187  
   188  type panicStep struct {
   189  	name           string
   190  	processed      bool
   191  	eventPublisher event.Publisher
   192  }
   193  
   194  func (s *panicStep) Name() string {
   195  	return s.name
   196  }
   197  
   198  func (s *panicStep) Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) {
   199  	s.eventPublisher.Publish(context.Background(), s.name)
   200  	logger.Infof("Panic!")
   201  	panic("Panicking just for test")
   202  	return operation, 0, nil
   203  }
   204  
   205  func fixProvisioningParametersWithPlanID(planID, region string) internal.ProvisioningParameters {
   206  	return internal.ProvisioningParameters{
   207  		PlanID:    planID,
   208  		ServiceID: "",
   209  		ErsContext: internal.ERSContext{
   210  			GlobalAccountID: globalAccountID,
   211  			SubAccountID:    subAccountID,
   212  		},
   213  		Parameters: internal.ProvisioningParametersDTO{
   214  			Region: ptr.String(region),
   215  			Name:   "dummy",
   216  			Zones:  []string{"europe-west3-b", "europe-west3-c"},
   217  		},
   218  	}
   219  }
   220  
   221  func FixOperation(ID string) internal.Operation {
   222  	operation := fixture.FixOperation(ID, "fea2c1a1-139d-43f6-910a-a618828a79d5", internal.OperationTypeProvision)
   223  	operation.FinishedStages = make([]string, 0)
   224  	operation.State = domain.InProgress
   225  	operation.Description = ""
   226  	operation.ProvisioningParameters = fixProvisioningParametersWithPlanID(broker.AzurePlanID, "westeurope")
   227  	return operation
   228  }
   229  
   230  type CollectingEventHandler struct {
   231  	mu             sync.Mutex
   232  	StepsProcessed []string // collects events from the Manager
   233  	stepsExecuted  []string // collects events from testing steps
   234  }
   235  
   236  func (h *CollectingEventHandler) OnStepExecuted(_ context.Context, ev interface{}) error {
   237  	h.mu.Lock()
   238  	defer h.mu.Unlock()
   239  	h.stepsExecuted = append(h.stepsExecuted, ev.(string))
   240  	return nil
   241  }
   242  
   243  func (h *CollectingEventHandler) OnStepProcessed(_ context.Context, ev interface{}) error {
   244  	h.mu.Lock()
   245  	defer h.mu.Unlock()
   246  	h.StepsProcessed = append(h.StepsProcessed, ev.(process.OperationStepProcessed).StepName)
   247  	return nil
   248  }
   249  
   250  func (h *CollectingEventHandler) Publish(ctx context.Context, ev interface{}) {
   251  	switch ev.(type) {
   252  	case process.OperationStepProcessed:
   253  		h.OnStepProcessed(ctx, ev)
   254  	case string:
   255  		h.OnStepExecuted(ctx, ev)
   256  	}
   257  }
   258  
   259  func (h *CollectingEventHandler) WaitForEvents(t *testing.T, count int) {
   260  	assert.NoError(t, wait.PollImmediate(time.Millisecond, time.Second, func() (bool, error) {
   261  		return len(h.StepsProcessed) == count, nil
   262  	}))
   263  }
   264  
   265  func (h *CollectingEventHandler) AssertProcessedSteps(t *testing.T, stepNames []string) {
   266  	h.WaitForEvents(t, len(stepNames))
   267  	h.mu.Lock()
   268  	defer h.mu.Unlock()
   269  
   270  	for i, stepName := range stepNames {
   271  		processed := h.StepsProcessed[i]
   272  		executed := h.stepsExecuted[i]
   273  		assert.Equal(t, stepName, processed)
   274  		assert.Equal(t, stepName, executed)
   275  	}
   276  	assert.Len(t, h.StepsProcessed, len(stepNames))
   277  }
   278  
   279  type resultCollector struct {
   280  	duration float64
   281  	state    domain.LastOperationState
   282  }
   283  
   284  func (rc *resultCollector) OnOperationSucceed(ctx context.Context, ev interface{}) error {
   285  	operationSucceeded, ok := ev.(process.OperationSucceeded)
   286  	if !ok {
   287  		return fmt.Errorf("expected process.OperationSucceeded but got %+v", ev)
   288  	}
   289  	op := operationSucceeded.Operation
   290  	minutes := op.UpdatedAt.Sub(op.CreatedAt).Minutes()
   291  	rc.duration = minutes
   292  	rc.state = op.State
   293  	return nil
   294  }
   295  
   296  func (rc *resultCollector) WaitForState(t *testing.T, state domain.LastOperationState) {
   297  	assert.NoError(t, wait.PollImmediate(time.Millisecond, 5*time.Second, func() (bool, error) {
   298  		return rc.state == state, nil
   299  	}))
   300  }
   301  
   302  func (rc *resultCollector) AssertSucceededState(t *testing.T) error {
   303  	assert.Equal(t, domain.Succeeded, rc.state)
   304  	return nil
   305  }
   306  
   307  func (rc *resultCollector) AssertDurationGreaterThanZero(t *testing.T) error {
   308  	assert.Greater(t, rc.duration, 0.0)
   309  	return nil
   310  }
   311  
   312  func SetupStagedManager2(op internal.Operation) (*process.StagedManager, storage.Operations, *event.PubSub) {
   313  	memoryStorage := storage.NewMemoryStorage()
   314  	memoryStorage.Operations().InsertOperation(op)
   315  
   316  	l := logrus.New()
   317  	l.SetLevel(logrus.DebugLevel)
   318  	pubSub := event.NewPubSub(nil)
   319  	mgr := process.NewStagedManager(memoryStorage.Operations(), pubSub, 3*time.Second, process.StagedManagerConfiguration{MaxStepProcessingTime: time.Second}, l)
   320  	mgr.SpeedUp(100000)
   321  	mgr.DefineStages([]string{"stage-1", "stage-2"})
   322  
   323  	return mgr, memoryStorage.Operations(), pubSub
   324  }
   325  
   326  func TestOperationSucceededEvent(t *testing.T) {
   327  	// given
   328  	const opID = "op-0001234"
   329  	operation := FixOperation("op-0001234")
   330  	mgr, _, pubSub := SetupStagedManager2(operation)
   331  	mgr.AddStep("stage-1", &testingStep{name: "first", eventPublisher: pubSub}, nil)
   332  
   333  	rc := &resultCollector{}
   334  	rc.duration = 123
   335  	pubSub.Subscribe(process.OperationSucceeded{}, rc.OnOperationSucceed)
   336  	fmt.Printf("rc: %.4f \n", rc.duration)
   337  
   338  	// when
   339  	mgr.Execute(operation.ID)
   340  
   341  	// then
   342  	rc.WaitForState(t, domain.Succeeded)
   343  	rc.AssertDurationGreaterThanZero(t)
   344  }