-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactor instance entity and repo; add cleanup infra (#52)
- Loading branch information
Showing
28 changed files
with
1,023 additions
and
527 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package cleanup | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
) | ||
|
||
type Cleaner interface { | ||
Clean(ctx context.Context) | ||
} | ||
|
||
type Manager struct { | ||
mutex sync.Mutex | ||
cleaners []Cleaner | ||
} | ||
|
||
func NewManager() *Manager { | ||
return &Manager{} | ||
} | ||
|
||
func (m *Manager) AddCleaner(c Cleaner) { | ||
m.mutex.Lock() | ||
defer m.mutex.Unlock() | ||
m.cleaners = append(m.cleaners, c) | ||
} | ||
|
||
func (m *Manager) Clean(ctx context.Context) { | ||
for _, c := range m.cleaners { | ||
go c.Clean(ctx) | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
internal/cleanup/cleaners/instancecleaner/instancecleaner.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package instancecleaner | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/jonboulle/clockwork" | ||
"github.com/rs/zerolog" | ||
|
||
"github.com/sergeii/swat4master/internal/cleanup" | ||
"github.com/sergeii/swat4master/internal/core/entities/filterset" | ||
"github.com/sergeii/swat4master/internal/core/repositories" | ||
"github.com/sergeii/swat4master/internal/metrics" | ||
) | ||
|
||
type Opts struct { | ||
Retention time.Duration | ||
} | ||
|
||
type InstanceCleaner struct { | ||
opts Opts | ||
instanceRepo repositories.InstanceRepository | ||
clock clockwork.Clock | ||
metrics *metrics.Collector | ||
logger *zerolog.Logger | ||
} | ||
|
||
func New( | ||
manager *cleanup.Manager, | ||
opts Opts, | ||
instanceRepo repositories.InstanceRepository, | ||
clock clockwork.Clock, | ||
metrics *metrics.Collector, | ||
logger *zerolog.Logger, | ||
) InstanceCleaner { | ||
cleaner := InstanceCleaner{ | ||
opts: opts, | ||
instanceRepo: instanceRepo, | ||
clock: clock, | ||
metrics: metrics, | ||
logger: logger, | ||
} | ||
manager.AddCleaner(&cleaner) | ||
return cleaner | ||
} | ||
|
||
func (c InstanceCleaner) Clean(ctx context.Context) { | ||
// Calculate the cutoff time for cleaning instances. | ||
cleanUntil := c.clock.Now().Add(-c.opts.Retention) | ||
fs := filterset.NewInstanceFilterSet().UpdatedBefore(cleanUntil) | ||
|
||
c.logger.Info().Stringer("until", cleanUntil).Msg("Starting to clean instances") | ||
|
||
count, err := c.instanceRepo.Clear(ctx, fs) | ||
if err != nil { | ||
c.metrics.CleanerErrors.WithLabelValues("instances").Inc() | ||
c.logger.Error().Err(err).Stringer("until", cleanUntil).Msg("Failed to clean instances") | ||
return | ||
} | ||
|
||
c.metrics.CleanerRemovals.WithLabelValues("instances").Add(float64(count)) | ||
c.logger.Info().Stringer("until", cleanUntil).Int("removed", count).Msg("Finished cleaning instances") | ||
} |
111 changes: 111 additions & 0 deletions
111
internal/cleanup/cleaners/instancecleaner/instancecleaner_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package instancecleaner_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/jonboulle/clockwork" | ||
"github.com/prometheus/client_golang/prometheus/testutil" | ||
"github.com/rs/zerolog" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
|
||
"github.com/sergeii/swat4master/internal/cleanup" | ||
"github.com/sergeii/swat4master/internal/cleanup/cleaners/instancecleaner" | ||
"github.com/sergeii/swat4master/internal/core/entities/filterset" | ||
"github.com/sergeii/swat4master/internal/core/repositories" | ||
"github.com/sergeii/swat4master/internal/metrics" | ||
) | ||
|
||
type MockInstanceRepository struct { | ||
mock.Mock | ||
repositories.InstanceRepository | ||
} | ||
|
||
func (m *MockInstanceRepository) Clear(ctx context.Context, fs filterset.InstanceFilterSet) (int, error) { | ||
args := m.Called(ctx, fs) | ||
return args.Get(0).(int), args.Error(1) // nolint: forcetypeassert | ||
} | ||
|
||
func TestInstanceCleaner_Clean_OK(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
manager := cleanup.NewManager() | ||
collector := metrics.New() | ||
clock := clockwork.NewFakeClock() | ||
logger := zerolog.Nop() | ||
options := instancecleaner.Opts{ | ||
Retention: time.Hour, | ||
} | ||
|
||
instanceRepo := new(MockInstanceRepository) | ||
instanceRepo.On("Clear", ctx, mock.Anything).Return(37, nil) | ||
|
||
cleaner := instancecleaner.New( | ||
manager, | ||
options, | ||
instanceRepo, | ||
clock, | ||
collector, | ||
&logger, | ||
) | ||
cleaner.Clean(ctx) | ||
|
||
instanceRepo.AssertCalled( | ||
t, | ||
"Clear", | ||
ctx, | ||
mock.MatchedBy(func(fs filterset.InstanceFilterSet) bool { | ||
updatedBefore, ok := fs.GetUpdatedBefore() | ||
wantUpdatedBefore := ok && updatedBefore.Equal(clock.Now().Add(-time.Hour)) | ||
return wantUpdatedBefore | ||
}), | ||
) | ||
|
||
cleanerRemovalsWithInstancesValue := testutil.ToFloat64(collector.CleanerRemovals.WithLabelValues("instances")) | ||
assert.Equal(t, float64(37), cleanerRemovalsWithInstancesValue) | ||
cleanerErrorsWithInstancesValue := testutil.ToFloat64(collector.CleanerErrors.WithLabelValues("instances")) | ||
assert.Equal(t, float64(0), cleanerErrorsWithInstancesValue) | ||
} | ||
|
||
func TestInstanceCleaner_Clean_RepoError(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
manager := cleanup.NewManager() | ||
collector := metrics.New() | ||
clock := clockwork.NewFakeClock() | ||
logger := zerolog.Nop() | ||
options := instancecleaner.Opts{ | ||
Retention: time.Hour, | ||
} | ||
|
||
instanceRepo := new(MockInstanceRepository) | ||
instanceRepo.On("Clear", ctx, mock.Anything).Return(0, assert.AnError) | ||
|
||
cleaner := instancecleaner.New( | ||
manager, | ||
options, | ||
instanceRepo, | ||
clock, | ||
collector, | ||
&logger, | ||
) | ||
cleaner.Clean(ctx) | ||
|
||
instanceRepo.AssertCalled( | ||
t, | ||
"Clear", | ||
ctx, | ||
mock.MatchedBy(func(fs filterset.InstanceFilterSet) bool { | ||
updatedBefore, ok := fs.GetUpdatedBefore() | ||
wantUpdatedBefore := ok && updatedBefore.Equal(clock.Now().Add(-time.Hour)) | ||
return wantUpdatedBefore | ||
}), | ||
) | ||
|
||
cleanerRemovalsWithInstancesValue := testutil.ToFloat64(collector.CleanerRemovals.WithLabelValues("instances")) | ||
assert.Equal(t, float64(0), cleanerRemovalsWithInstancesValue) | ||
cleanerErrorsWithInstancesValue := testutil.ToFloat64(collector.CleanerErrors.WithLabelValues("instances")) | ||
assert.Equal(t, float64(1), cleanerErrorsWithInstancesValue) | ||
} |
Oops, something went wrong.