Skip to content

Commit bac23e0

Browse files
ishanarya0Ishan Arya
and
Ishan Arya
authored
feat: add dry run (#116)
* feat: add dry run * feat: add swagger * test: add dry run tests * refactor: use option pattern * tests: fix call * tests: fix params --------- Co-authored-by: Ishan Arya <[email protected]>
1 parent eb750e2 commit bac23e0

File tree

10 files changed

+551
-269
lines changed

10 files changed

+551
-269
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
NAME=github.com/goto/entropy
22
VERSION=$(shell git describe --tags --always --first-parent 2>/dev/null)
33
COMMIT=$(shell git rev-parse --short HEAD)
4-
PROTON_COMMIT="564a3d2fa0aa14e435dc4f264fbf63ff2dcf09c2"
4+
PROTON_COMMIT="6c5bc2b621abe2812cc8288a5f6363570bab911a"
55
BUILD_TIME=$(shell date)
66
COVERAGE_DIR=coverage
77
BUILD_DIR=dist

core/write.go

+31-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ import (
99
"github.com/goto/entropy/pkg/errors"
1010
)
1111

12-
func (svc *Service) CreateResource(ctx context.Context, res resource.Resource) (*resource.Resource, error) {
12+
type Options struct {
13+
DryRun bool
14+
}
15+
16+
func WithDryRun(dryRun bool) Options {
17+
return Options{DryRun: dryRun}
18+
}
19+
20+
func (svc *Service) CreateResource(ctx context.Context, res resource.Resource, resourceOpts ...Options) (*resource.Resource, error) {
1321
if err := res.Validate(true); err != nil {
1422
return nil, err
1523
}
@@ -22,10 +30,15 @@ func (svc *Service) CreateResource(ctx context.Context, res resource.Resource) (
2230
}
2331
res.Spec.Configs = nil
2432

25-
return svc.execAction(ctx, res, act)
33+
dryRun := false
34+
for _, opt := range resourceOpts {
35+
dryRun = opt.DryRun
36+
}
37+
38+
return svc.execAction(ctx, res, act, dryRun)
2639
}
2740

28-
func (svc *Service) UpdateResource(ctx context.Context, urn string, req resource.UpdateRequest) (*resource.Resource, error) {
41+
func (svc *Service) UpdateResource(ctx context.Context, urn string, req resource.UpdateRequest, resourceOpts ...Options) (*resource.Resource, error) {
2942
if len(req.Spec.Dependencies) != 0 {
3043
return nil, errors.ErrUnsupported.WithMsgf("updating dependencies is not supported")
3144
} else if len(req.Spec.Configs) == 0 {
@@ -37,17 +50,17 @@ func (svc *Service) UpdateResource(ctx context.Context, urn string, req resource
3750
Params: req.Spec.Configs,
3851
Labels: req.Labels,
3952
UserID: req.UserID,
40-
})
53+
}, resourceOpts...)
4154
}
4255

4356
func (svc *Service) DeleteResource(ctx context.Context, urn string) error {
4457
_, actionErr := svc.ApplyAction(ctx, urn, module.ActionRequest{
4558
Name: module.DeleteAction,
46-
})
59+
}, WithDryRun(false))
4760
return actionErr
4861
}
4962

50-
func (svc *Service) ApplyAction(ctx context.Context, urn string, act module.ActionRequest) (*resource.Resource, error) {
63+
func (svc *Service) ApplyAction(ctx context.Context, urn string, act module.ActionRequest, resourceOpts ...Options) (*resource.Resource, error) {
5164
res, err := svc.GetResource(ctx, urn)
5265
if err != nil {
5366
return nil, err
@@ -56,10 +69,15 @@ func (svc *Service) ApplyAction(ctx context.Context, urn string, act module.Acti
5669
WithMsgf("cannot perform '%s' on resource in '%s'", act.Name, res.State.Status)
5770
}
5871

59-
return svc.execAction(ctx, *res, act)
72+
dryRun := false
73+
for _, opt := range resourceOpts {
74+
dryRun = opt.DryRun
75+
}
76+
77+
return svc.execAction(ctx, *res, act, dryRun)
6078
}
6179

62-
func (svc *Service) execAction(ctx context.Context, res resource.Resource, act module.ActionRequest) (*resource.Resource, error) {
80+
func (svc *Service) execAction(ctx context.Context, res resource.Resource, act module.ActionRequest, dryRun bool) (*resource.Resource, error) {
6381
planned, err := svc.planChange(ctx, res, act)
6482
if err != nil {
6583
return nil, err
@@ -77,8 +95,11 @@ func (svc *Service) execAction(ctx context.Context, res resource.Resource, act m
7795
}
7896

7997
reason := fmt.Sprintf("action:%s", act.Name)
80-
if err := svc.upsert(ctx, *planned, isCreate(act.Name), true, reason); err != nil {
81-
return nil, err
98+
99+
if !dryRun {
100+
if err := svc.upsert(ctx, *planned, isCreate(act.Name), true, reason); err != nil {
101+
return nil, err
102+
}
82103
}
83104
return planned, nil
84105
}

core/write_test.go

+152-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestService_CreateResource(t *testing.T) {
2626
setup func(t *testing.T) *core.Service
2727
res resource.Resource
2828
want *resource.Resource
29+
options []core.Options
2930
wantErr error
3031
}{
3132
{
@@ -278,6 +279,41 @@ func TestService_CreateResource(t *testing.T) {
278279
},
279280
wantErr: nil,
280281
},
282+
{
283+
name: "AlreadyExistsWithDryRun",
284+
setup: func(t *testing.T) *core.Service {
285+
t.Helper()
286+
mod := &mocks.ModuleService{}
287+
mod.EXPECT().
288+
PlanAction(mock.Anything, mock.Anything, mock.Anything).
289+
Return(&resource.Resource{
290+
Kind: "mock",
291+
Name: "child",
292+
Project: "project",
293+
State: resource.State{Status: resource.StatusCompleted},
294+
}, nil).Once()
295+
296+
resourceRepo := &mocks.ResourceStore{}
297+
298+
return core.New(resourceRepo, mod, deadClock, defaultSyncBackoff, defaultMaxRetries)
299+
},
300+
res: resource.Resource{
301+
Kind: "mock",
302+
Name: "child",
303+
Project: "project",
304+
},
305+
want: &resource.Resource{
306+
URN: "orn:entropy:mock:project:child",
307+
Kind: "mock",
308+
Name: "child",
309+
Project: "project",
310+
State: resource.State{Status: resource.StatusCompleted},
311+
CreatedAt: frozenTime,
312+
UpdatedAt: frozenTime,
313+
},
314+
options: []core.Options{core.WithDryRun(true)},
315+
wantErr: nil,
316+
},
281317
}
282318

283319
for _, tt := range tests {
@@ -286,7 +322,7 @@ func TestService_CreateResource(t *testing.T) {
286322
t.Parallel()
287323
svc := tt.setup(t)
288324

289-
got, err := svc.CreateResource(context.Background(), tt.res)
325+
got, err := svc.CreateResource(context.Background(), tt.res, tt.options...)
290326
if tt.wantErr != nil {
291327
assert.Error(t, err)
292328
assert.True(t, errors.Is(err, tt.wantErr))
@@ -310,12 +346,25 @@ func TestService_UpdateResource(t *testing.T) {
310346
CreatedAt: frozenTime,
311347
}
312348

349+
testResourceForDryRun := resource.Resource{
350+
URN: "orn:entropy:mock:project:childtwo",
351+
Kind: "mock",
352+
Name: "childtwo",
353+
Project: "project",
354+
State: resource.State{Status: resource.StatusCompleted},
355+
Spec: resource.Spec{
356+
Configs: []byte(`{"foo": "bar-old"}`),
357+
},
358+
CreatedAt: frozenTime,
359+
}
360+
313361
tests := []struct {
314362
name string
315363
setup func(t *testing.T) *core.Service
316364
urn string
317365
update resource.UpdateRequest
318366
want *resource.Resource
367+
options []core.Options
319368
wantErr error
320369
}{
321370
{
@@ -472,6 +521,57 @@ func TestService_UpdateResource(t *testing.T) {
472521
},
473522
wantErr: nil,
474523
},
524+
{
525+
name: "SuccessWithDryRun",
526+
setup: func(t *testing.T) *core.Service {
527+
t.Helper()
528+
mod := &mocks.ModuleService{}
529+
mod.EXPECT().
530+
PlanAction(mock.Anything, mock.Anything, mock.Anything).
531+
Return(&resource.Resource{
532+
URN: "orn:entropy:mock:project:childtwo",
533+
Kind: "mock",
534+
Name: "childtwo",
535+
Project: "project",
536+
Spec: resource.Spec{
537+
Configs: []byte(`{"foo": "bar"}`),
538+
},
539+
State: resource.State{Status: resource.StatusPending},
540+
CreatedAt: frozenTime,
541+
}, nil).Once()
542+
mod.EXPECT().
543+
GetOutput(mock.Anything, mock.Anything).
544+
Return(nil, nil).
545+
Once()
546+
547+
resourceRepo := &mocks.ResourceStore{}
548+
resourceRepo.EXPECT().
549+
GetByURN(mock.Anything, "orn:entropy:mock:project:childtwo").
550+
Return(&testResourceForDryRun, nil).Once()
551+
552+
return core.New(resourceRepo, mod, deadClock, defaultSyncBackoff, defaultMaxRetries)
553+
},
554+
urn: "orn:entropy:mock:project:childtwo",
555+
update: resource.UpdateRequest{
556+
Spec: resource.Spec{Configs: []byte(`{"foo": "bar"}`)},
557+
Labels: map[string]string{"created_by": "test_user", "group": "test_group"},
558+
},
559+
want: &resource.Resource{
560+
URN: "orn:entropy:mock:project:childtwo",
561+
Kind: "mock",
562+
Name: "childtwo",
563+
Project: "project",
564+
CreatedAt: frozenTime,
565+
UpdatedAt: frozenTime,
566+
State: resource.State{Status: resource.StatusPending},
567+
Labels: map[string]string{"created_by": "test_user", "group": "test_group"},
568+
Spec: resource.Spec{
569+
Configs: []byte(`{"foo": "bar"}`),
570+
},
571+
},
572+
options: []core.Options{core.WithDryRun(true)},
573+
wantErr: nil,
574+
},
475575
}
476576

477577
for _, tt := range tests {
@@ -480,7 +580,7 @@ func TestService_UpdateResource(t *testing.T) {
480580
t.Parallel()
481581
svc := tt.setup(t)
482582

483-
got, err := svc.UpdateResource(context.Background(), tt.urn, tt.update)
583+
got, err := svc.UpdateResource(context.Background(), tt.urn, tt.update, tt.options...)
484584
if tt.wantErr != nil {
485585
assert.Error(t, err)
486586
assert.True(t, errors.Is(err, tt.wantErr))
@@ -641,6 +741,7 @@ func TestService_ApplyAction(t *testing.T) {
641741
urn string
642742
action module.ActionRequest
643743
want *resource.Resource
744+
options []core.Options
644745
wantErr error
645746
}{
646747
{
@@ -771,6 +872,54 @@ func TestService_ApplyAction(t *testing.T) {
771872
},
772873
wantErr: nil,
773874
},
875+
{
876+
name: "SuccessWithDryRun",
877+
setup: func(t *testing.T) *core.Service {
878+
t.Helper()
879+
mod := &mocks.ModuleService{}
880+
mod.EXPECT().
881+
PlanAction(mock.Anything, mock.Anything, sampleAction).
882+
Return(&resource.Resource{
883+
URN: "orn:entropy:mock:foo:bar",
884+
Kind: "mock",
885+
Project: "foo",
886+
Name: "bar",
887+
State: resource.State{Status: resource.StatusPending},
888+
}, nil).Once()
889+
mod.EXPECT().
890+
GetOutput(mock.Anything, mock.Anything).
891+
Return(nil, nil).
892+
Once()
893+
894+
resourceRepo := &mocks.ResourceStore{}
895+
resourceRepo.EXPECT().
896+
GetByURN(mock.Anything, "orn:entropy:mock:foo:bar").
897+
Return(&resource.Resource{
898+
URN: "orn:entropy:mock:foo:bar",
899+
Kind: "mock",
900+
Project: "foo",
901+
Name: "bar",
902+
CreatedAt: frozenTime,
903+
State: resource.State{Status: resource.StatusCompleted},
904+
}, nil).
905+
Once()
906+
907+
return core.New(resourceRepo, mod, deadClock, defaultSyncBackoff, defaultMaxRetries)
908+
},
909+
urn: "orn:entropy:mock:foo:bar",
910+
action: sampleAction,
911+
want: &resource.Resource{
912+
URN: "orn:entropy:mock:foo:bar",
913+
Kind: "mock",
914+
Project: "foo",
915+
Name: "bar",
916+
State: resource.State{Status: resource.StatusPending},
917+
CreatedAt: frozenTime,
918+
UpdatedAt: frozenTime,
919+
},
920+
wantErr: nil,
921+
options: []core.Options{core.WithDryRun(true)},
922+
},
774923
}
775924

776925
for _, tt := range tests {
@@ -779,7 +928,7 @@ func TestService_ApplyAction(t *testing.T) {
779928
t.Parallel()
780929
svc := tt.setup(t)
781930

782-
got, err := svc.ApplyAction(context.Background(), tt.urn, tt.action)
931+
got, err := svc.ApplyAction(context.Background(), tt.urn, tt.action, tt.options...)
783932
if tt.wantErr != nil {
784933
assert.Error(t, err)
785934
assert.True(t, errors.Is(err, tt.wantErr), cmp.Diff(tt.want, err))

0 commit comments

Comments
 (0)