diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go index 6101e1db13f40027706977dad4b710261d82439d..b2999579758d90c51dce5da0d9a3462db444dc9b 100644 --- a/pkg/setup/setup.go +++ b/pkg/setup/setup.go @@ -2,13 +2,14 @@ package setup import ( "context" - "git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/clients" "git.perx.ru/perxis/perxis-go/pkg/collections" "git.perx.ru/perxis/perxis-go/pkg/content" + "git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/items" "git.perx.ru/perxis/perxis-go/pkg/roles" + "git.perx.ru/perxis/perxis-go/pkg/spaces" "go.uber.org/zap" ) @@ -146,52 +147,67 @@ func (s *Setup) AddItem(item *items.Item, opt ...ItemsOption) *Setup { // Install выполняет установку необходимых требований func (s *Setup) Install(ctx context.Context) error { - if err := s.InstallRoles(ctx); err != nil { - return err - } - if err := s.InstallClients(ctx); err != nil { - return err - } - if err := s.InstallCollections(ctx); err != nil { - return err - } - if err := s.InstallItems(ctx); err != nil { - return err - } - return nil + err := spaces.CheckIsSpaceAvailableWithRetry(ctx, s.content.Spaces, s.SpaceID, s.logger) + if err == nil { + if err := s.InstallRoles(ctx); err != nil { + return err + } + if err := s.InstallClients(ctx); err != nil { + return err + } + if err := s.InstallCollections(ctx); err != nil { + return err + } + if err := s.InstallItems(ctx); err != nil { + return err + } + s.logger.Info("Install finished", zap.String(s.SpaceID, "spaceID")) + return nil + } + return err } // Check выполняет проверку требований func (s *Setup) Check(ctx context.Context) error { - if err := s.CheckRoles(ctx); err != nil { - return err - } - if err := s.CheckClients(ctx); err != nil { - return err - } - if err := s.CheckCollections(ctx); err != nil { - return err - } - if err := s.CheckItems(ctx); err != nil { - return err - } - return nil + err := spaces.CheckIsReadAvailableWithRetry(ctx, s.content.Spaces, s.SpaceID, s.logger) + if err == nil { + if err := s.CheckRoles(ctx); err != nil { + return err + } + if err := s.CheckClients(ctx); err != nil { + return err + } + if err := s.CheckCollections(ctx); err != nil { + return err + } + if err := s.CheckItems(ctx); err != nil { + return err + } + return nil + } + return err + } // Uninstall выполняет удаление установленных раннее требований func (s *Setup) Uninstall(ctx context.Context) error { - // В случае если необходимо удалить данные удаляем все что создано при установке расширения - if err := s.UninstallClients(ctx); err != nil { - return err - } - if err := s.UninstallRoles(ctx); err != nil { - return err - } - if err := s.UninstallCollections(ctx); err != nil { - return err - } - if err := s.UninstallItems(ctx); err != nil { - return err - } - return nil + err := spaces.CheckIsSpaceAvailableWithRetry(ctx, s.content.Spaces, s.SpaceID, s.logger) + if err == nil { + // В случае если необходимо удалить данные удаляем все что создано при установке расширения + if err := s.UninstallClients(ctx); err != nil { + return err + } + if err := s.UninstallRoles(ctx); err != nil { + return err + } + if err := s.UninstallCollections(ctx); err != nil { + return err + } + if err := s.UninstallItems(ctx); err != nil { + return err + } + s.logger.Info("Uninstall finished", zap.String(s.SpaceID, "spaceID")) + return nil + } + return err } diff --git a/pkg/setup/setup_test.go b/pkg/setup/setup_test.go index 681158887cb53e81582615146b30e68cb2cdef67..82d4849e2b5217e3ba576532eee1a4d52ef27174 100644 --- a/pkg/setup/setup_test.go +++ b/pkg/setup/setup_test.go @@ -17,6 +17,8 @@ import ( "git.perx.ru/perxis/perxis-go/pkg/roles" rolesMock "git.perx.ru/perxis/perxis-go/pkg/roles/mocks" "git.perx.ru/perxis/perxis-go/pkg/schema" + "git.perx.ru/perxis/perxis-go/pkg/spaces" + "git.perx.ru/perxis/perxis-go/pkg/spaces/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -77,7 +79,6 @@ func getActions() []*items.Item { func newSetup(content *content.Content, t *testing.T) *Setup { logger := zaptest.NewLogger(t, zaptest.WrapOptions()) - setup := NewSetup(content, spaceID, envID, logger) setup.AddCollections(getCollections()) setup.AddRoles(getRoles()) @@ -88,14 +89,23 @@ func newSetup(content *content.Content, t *testing.T) *Setup { } func TestSetupInstall(t *testing.T) { + sps := &spaces.Space{ + ID: spaceID, + OrgID: "org", + State: spaces.StateReady, + } + t.Run("Success, nothing to install", func(t *testing.T) { logger := zaptest.NewLogger(t, zaptest.WrapOptions()) - - setup := NewSetup(nil, spaceID, envID, logger) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) err := setup.Install(context.Background()) require.NoError(t, err) + + sp.AssertExpectations(t) }) t.Run("Success, no force", func(t *testing.T) { @@ -155,12 +165,16 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, Environments: envMocks, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -173,6 +187,7 @@ func TestSetupInstall(t *testing.T) { clMock.AssertExpectations(t) itmMock.AssertExpectations(t) envMocks.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Success, update existing records", func(t *testing.T) { @@ -216,12 +231,16 @@ func TestSetupInstall(t *testing.T) { Return(nil). Times(len(itms)) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, Environments: envMocks, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -234,6 +253,7 @@ func TestSetupInstall(t *testing.T) { clMock.AssertExpectations(t) itmMock.AssertExpectations(t) envMocks.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Success, with force", func(t *testing.T) { @@ -270,11 +290,15 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithForce(true) @@ -295,8 +319,12 @@ func TestSetupInstall(t *testing.T) { Return(nil, errors.New("can't get role")). Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ - Roles: rMock, + Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -305,6 +333,7 @@ func TestSetupInstall(t *testing.T) { assert.ErrorContains(t, err, "failed to install role") rMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't install client, storage returns error", func(t *testing.T) { @@ -326,9 +355,13 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Clients: clMock, Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -338,6 +371,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't get collection, storage returns error", func(t *testing.T) { @@ -370,10 +404,14 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -384,6 +422,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't create collection, storage returns error", func(t *testing.T) { @@ -420,10 +459,14 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -434,6 +477,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't update collection, storage returns error", func(t *testing.T) { @@ -463,10 +507,14 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -477,6 +525,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't set schema, storage returns error", func(t *testing.T) { @@ -517,10 +566,14 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -531,6 +584,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't migrate, storage returns error", func(t *testing.T) { @@ -576,11 +630,15 @@ func TestSetupInstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Environments: envMocks, + Spaces: sp, }, t) err := setup.Install(context.Background()) @@ -591,6 +649,7 @@ func TestSetupInstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't find action, storage returns error", func(t *testing.T) { @@ -638,12 +697,16 @@ func TestSetupInstall(t *testing.T) { Return(nil, errors.New("can't create item")). Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, Environments: envMocks, + Spaces: sp, }, t) setup = setup.WithForce(true) @@ -659,6 +722,34 @@ func TestSetupInstall(t *testing.T) { envMocks.AssertExpectations(t) }) + t.Run("Success on retry when space not available", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StateMigration}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Install(context.Background()) + + require.NoError(t, err) + + sp.AssertExpectations(t) + }) + + t.Run("Error on retry", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StateMigration}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(nil, errors.New("some error")).Once() + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Install(context.Background()) + + require.Error(t, err, "в момент выполнения пришла ошибка отличная от 'space not available'") + + sp.AssertExpectations(t) + }) + //t.Run("Can't find task configs, storage returns error", func(t *testing.T) { // envMocks := &environmentMock.Environments{} // envMocks.On("Migrate", mock.Anything, spaceID, envID). @@ -738,14 +829,25 @@ func TestSetupInstall(t *testing.T) { } func TestSetupUninstall(t *testing.T) { + sps := &spaces.Space{ + ID: spaceID, + OrgID: "org", + State: spaces.StateReady, + } + t.Run("Success, nothing to uninstall", func(t *testing.T) { logger := zaptest.NewLogger(t, zaptest.WrapOptions()) - setup := NewSetup(nil, spaceID, envID, logger) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) err := setup.Uninstall(context.Background()) require.NoError(t, err) + + sp.AssertExpectations(t) }) t.Run("Remove", func(t *testing.T) { @@ -781,11 +883,15 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -798,6 +904,7 @@ func TestSetupUninstall(t *testing.T) { collsMock.AssertExpectations(t) clMock.AssertExpectations(t) itmMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Remove, with clients NotFound error", func(t *testing.T) { @@ -833,11 +940,15 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -850,6 +961,7 @@ func TestSetupUninstall(t *testing.T) { collsMock.AssertExpectations(t) clMock.AssertExpectations(t) itmMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't uninstall clients, storage returns error", func(t *testing.T) { @@ -867,10 +979,14 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -881,6 +997,7 @@ func TestSetupUninstall(t *testing.T) { rMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't uninstall role, storage returns error", func(t *testing.T) { @@ -901,10 +1018,14 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Roles: rMock, Clients: clMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -914,6 +1035,7 @@ func TestSetupUninstall(t *testing.T) { assert.ErrorContains(t, err, "failed to uninstall role") rMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't uninstall collections, storage returns error", func(t *testing.T) { @@ -941,11 +1063,15 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -957,6 +1083,7 @@ func TestSetupUninstall(t *testing.T) { rMock.AssertExpectations(t) collsMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't uninstall actions, storage returns error", func(t *testing.T) { @@ -990,11 +1117,15 @@ func TestSetupUninstall(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) setup = setup.WithRemove(true) @@ -1004,18 +1135,62 @@ func TestSetupUninstall(t *testing.T) { assert.ErrorContains(t, err, "failed to uninstall item") itmMock.AssertExpectations(t) + sp.AssertExpectations(t) + }) + + t.Run("Success on retry when space not available", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StateMigration}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Uninstall(context.Background()) + + require.NoError(t, err) + + sp.AssertExpectations(t) + }) + + t.Run("Error on retry", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StateMigration}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(nil, errors.New("some error")).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Uninstall(context.Background()) + + require.Error(t, err, "в момент выполнения пришла ошибка отличная от 'space not available'") + + sp.AssertExpectations(t) }) } func TestSetupCheck(t *testing.T) { + sps := &spaces.Space{ + ID: spaceID, + OrgID: "org", + State: spaces.StateReady, + } + t.Run("Success, nothing to check", func(t *testing.T) { logger := zaptest.NewLogger(t, zaptest.WrapOptions()) - setup := NewSetup(nil, spaceID, envID, logger) + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) err := setup.Check(context.Background()) require.NoError(t, err) + + sp.AssertExpectations(t) }) t.Run("Success", func(t *testing.T) { @@ -1051,11 +1226,15 @@ func TestSetupCheck(t *testing.T) { mock.MatchedBy(func(opt *items.FindOptions) bool { return opt.Regular && opt.Hidden && opt.Templates }), ).Return(getActions(), 0, nil).Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Collections: collsMock, Clients: clMock, Roles: rMock, Items: itmMock, + Spaces: sp, }, t) err := setup.Check(context.Background()) @@ -1067,6 +1246,7 @@ func TestSetupCheck(t *testing.T) { collsMock.AssertExpectations(t) clMock.AssertExpectations(t) itmMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't get role, storage returns error", func(t *testing.T) { @@ -1075,8 +1255,12 @@ func TestSetupCheck(t *testing.T) { Return(nil, errors.New("can't get role")). Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ - Roles: rMock, + Roles: rMock, + Spaces: sp, }, t) err := setup.Check(context.Background()) @@ -1085,6 +1269,7 @@ func TestSetupCheck(t *testing.T) { assert.ErrorContains(t, err, "role check error") rMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't get client, storage returns error", func(t *testing.T) { @@ -1100,9 +1285,13 @@ func TestSetupCheck(t *testing.T) { Return(nil, errors.New("can't get client")). Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Roles: rMock, Clients: clMock, + Spaces: sp, }, t) err := setup.Check(context.Background()) @@ -1112,6 +1301,7 @@ func TestSetupCheck(t *testing.T) { rMock.AssertExpectations(t) clMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't get collection, storage returns error", func(t *testing.T) { @@ -1134,10 +1324,14 @@ func TestSetupCheck(t *testing.T) { Once() } + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Roles: rMock, Clients: clMock, Collections: collsMock, + Spaces: sp, }, t) err := setup.Check(context.Background()) @@ -1148,6 +1342,7 @@ func TestSetupCheck(t *testing.T) { rMock.AssertExpectations(t) clMock.AssertExpectations(t) collsMock.AssertExpectations(t) + sp.AssertExpectations(t) }) t.Run("Can't get action, storage returns error", func(t *testing.T) { @@ -1177,11 +1372,15 @@ func TestSetupCheck(t *testing.T) { Return(nil, 0, nil). Once() + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + setup := newSetup(&content.Content{ Roles: rMock, Clients: clMock, Collections: collsMock, Items: itmMock, + Spaces: sp, }, t) err := setup.Check(context.Background()) @@ -1192,6 +1391,39 @@ func TestSetupCheck(t *testing.T) { rMock.AssertExpectations(t) clMock.AssertExpectations(t) collsMock.AssertExpectations(t) + sp.AssertExpectations(t) + }) + + t.Run("Success on retry when space is preparing", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StatePreparing}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Check(context.Background()) + + require.NoError(t, err) + + sp.AssertExpectations(t) + }) + + t.Run("Error on retry", func(t *testing.T) { + logger := zaptest.NewLogger(t, zaptest.WrapOptions()) + + sp := &mocks.Spaces{} + sp.On("Get", mock.Anything, mock.Anything).Return(&spaces.Space{ID: spaceID, OrgID: "org", State: spaces.StatePreparing}, nil).Twice() + sp.On("Get", mock.Anything, mock.Anything).Return(nil, errors.New("some error")).Once() + + setup := NewSetup(&content.Content{Spaces: sp}, spaceID, envID, logger) + + err := setup.Check(context.Background()) + + require.Error(t, err, "в момент выполнения пришла ошибка отличная от 'Space is migrating'") + + sp.AssertExpectations(t) }) //t.Run("Can't get task config, storage returns error", func(t *testing.T) { diff --git a/pkg/spaces/service.go b/pkg/spaces/service.go index 2c17dbfaa22ddeff6cc5cc7f6efd6b9ff4c119b5..6f3e0eab98fe14d5a23ad024a51c3b48a5a22d7b 100644 --- a/pkg/spaces/service.go +++ b/pkg/spaces/service.go @@ -2,8 +2,12 @@ package spaces import ( "context" + "strings" + "time" + "github.com/avast/retry-go/v4" "github.com/pkg/errors" + "go.uber.org/zap" ) // @microgen grpc @@ -53,7 +57,7 @@ type Spaces interface { func IsSpaceAvailable(ctx context.Context, svc Spaces, spaceID string) error { sp, err := svc.Get(ctx, spaceID) if err != nil { - return errors.Wrap(err, "space not available") + return errors.Wrap(err, "fail to get space") } if sp.State == StateReady || sp.State == StateNew { return nil @@ -71,3 +75,35 @@ func IsReadAvailable(ctx context.Context, svc Spaces, spaceID string) error { } return errors.Errorf("space '%s' is migrating, fail to do operation", sp.ID) } + +func CheckIsSpaceAvailableWithRetry(ctx context.Context, svc Spaces, spaceID string, logger *zap.Logger) error { + return retry.Do( + func() error { + return IsSpaceAvailable(ctx, svc, spaceID) + }, + retry.RetryIf(func(err error) bool { return strings.Contains(err.Error(), "space not available") }), + retry.OnRetry(func(n uint, err error) { + logger.Warn("Space not available", zap.String("spaceID", spaceID), zap.Uint("Retry", n), zap.Error(err)) + }), + retry.DelayType(retry.BackOffDelay), + retry.MaxDelay(1*time.Minute), + retry.Attempts(20), + retry.Context(ctx), + ) +} + +func CheckIsReadAvailableWithRetry(ctx context.Context, svc Spaces, spaceID string, logger *zap.Logger) error { + return retry.Do( + func() error { + return IsReadAvailable(ctx, svc, spaceID) + }, + retry.RetryIf(func(err error) bool { return strings.Contains(err.Error(), "is migrating") }), + retry.OnRetry(func(n uint, err error) { + logger.Warn("Space is migrating", zap.String("spaceID", spaceID), zap.Uint("Retry", n), zap.Error(err)) + }), + retry.DelayType(retry.BackOffDelay), + retry.MaxDelay(1*time.Minute), + retry.Attempts(20), + retry.Context(ctx), + ) +}