Skip to content

Commit 252dafe

Browse files
authored
Add retention flag, call to update retention (#93)
* Add retention flag, call to update retention * Add flags * Use set subcommand with retention command to mirror convention elsewhere * Update variable name * Change alias * Update documentation * Correct flag type * Update variable name, update documentation * Add get retention for tcld * Add resource version * Add uint flag for retention * Add retention day bounds * Add tests * Remove requestID from get command
1 parent 1754f08 commit 252dafe

3 files changed

Lines changed: 253 additions & 0 deletions

File tree

app/flags.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
const (
1111
ServerFlagName = "server"
1212
ConfigDirFlagName = "config-dir"
13+
RetentionDaysFlagName = "retention-days"
1314
NamespaceFlagName = "namespace"
1415
RequestIDFlagName = "request-id"
1516
ResourceVersionFlagName = "resource-version"
@@ -32,6 +33,13 @@ var (
3233
Hidden: true,
3334
Required: false,
3435
}
36+
RetentionDaysFlag = &cli.IntFlag{
37+
Name: RetentionDaysFlagName,
38+
Usage: "The retention of the namespace in days",
39+
Aliases: []string{"rd"},
40+
EnvVars: []string{"NAMESPACE_RETENTION"},
41+
Required: true,
42+
}
3543
NamespaceFlag = &cli.StringFlag{
3644
Name: NamespaceFlagName,
3745
Usage: "The namespace hosted on temporal cloud",

app/namespace.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,58 @@ func NewNamespaceCommand(getNamespaceClientFn GetNamespaceClientFn) (CommandOut,
574574
},
575575
},
576576
},
577+
{
578+
Name: "retention",
579+
Usage: "Manages configuration of the length of time (in days) a closed workflow will be preserved before deletion",
580+
Aliases: []string{"r"},
581+
Subcommands: []*cli.Command{
582+
{
583+
Name: "set",
584+
Aliases: []string{"s"},
585+
Usage: "Set the length of time (in days) a closed workflow will be preserved before deletion for a given namespace",
586+
Flags: []cli.Flag{
587+
NamespaceFlag,
588+
ResourceVersionFlag,
589+
RetentionDaysFlag,
590+
RequestIDFlag,
591+
},
592+
Action: func(ctx *cli.Context) error {
593+
retention := ctx.Int(RetentionDaysFlagName)
594+
if retention == 0 {
595+
return fmt.Errorf("retention must be at least 1 day in duration")
596+
}
597+
if retention < 0 {
598+
return fmt.Errorf("retention cannot be negative")
599+
}
600+
n, err := c.getNamespace(ctx.String(NamespaceFlagName))
601+
if err != nil {
602+
return err
603+
}
604+
if int32(retention) == n.Spec.RetentionDays {
605+
return fmt.Errorf("retention for namespace is already set at %d days", ctx.Int(RetentionDaysFlagName))
606+
}
607+
n.Spec.RetentionDays = int32(retention)
608+
return c.updateNamespace(ctx, n)
609+
},
610+
},
611+
{
612+
Name: "get",
613+
Aliases: []string{"g"},
614+
Usage: "Retrieve the length of time (in days) a closed workflow will be preserved before deletion for a given namespace",
615+
Flags: []cli.Flag{
616+
NamespaceFlag,
617+
},
618+
Action: func(ctx *cli.Context) error {
619+
n, err := c.getNamespace(ctx.String(NamespaceFlagName))
620+
if err != nil {
621+
return err
622+
}
623+
fmt.Println(n.Spec.RetentionDays)
624+
return nil
625+
},
626+
},
627+
},
628+
},
577629
{
578630
Name: "search-attributes",
579631
Usage: "Manage search attributes used by namespace",

app/namespace_test.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,3 +1056,196 @@ func (s *NamespaceTestSuite) TestClearCertificateFilters() {
10561056
})
10571057
}
10581058
}
1059+
1060+
func (s *NamespaceTestSuite) TestUpdateNamespaceRetention() {
1061+
1062+
ns := "ns1"
1063+
type morphGetResp func(*namespaceservice.GetNamespaceResponse)
1064+
type morphUpdateReq func(*namespaceservice.UpdateNamespaceRequest)
1065+
1066+
tests := []struct {
1067+
name string
1068+
args []string
1069+
expectGet morphGetResp
1070+
expectErr bool
1071+
expectUpdate morphUpdateReq
1072+
}{
1073+
{
1074+
name: "displays help",
1075+
args: []string{"namespace", "retention"},
1076+
},
1077+
{
1078+
name: "missing flags",
1079+
args: []string{"namespace", "retention", "set"},
1080+
expectErr: true,
1081+
},
1082+
{
1083+
name: "missing retention-days flag",
1084+
args: []string{"namespace", "retention", "set", "--namespace", ns},
1085+
expectErr: true,
1086+
},
1087+
{
1088+
name: "happy path",
1089+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "7"},
1090+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {},
1091+
expectUpdate: func(r *namespaceservice.UpdateNamespaceRequest) {
1092+
r.Spec.RetentionDays = 7
1093+
},
1094+
expectErr: false,
1095+
},
1096+
{
1097+
name: "aliases",
1098+
args: []string{"n", "r", "s", "-n", ns, "-rd", "7"},
1099+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {},
1100+
expectUpdate: func(r *namespaceservice.UpdateNamespaceRequest) {
1101+
r.Spec.RetentionDays = 7
1102+
},
1103+
expectErr: false,
1104+
},
1105+
{
1106+
name: "invalid day negative",
1107+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "-7"},
1108+
expectErr: true,
1109+
},
1110+
{
1111+
name: "invalid day 0",
1112+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "0"},
1113+
expectErr: true,
1114+
},
1115+
{
1116+
name: "no namespace found",
1117+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "7"},
1118+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {
1119+
g.Namespace = nil
1120+
},
1121+
expectErr: true,
1122+
},
1123+
{
1124+
name: "retention unchanged",
1125+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "10"},
1126+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {},
1127+
expectErr: true,
1128+
},
1129+
}
1130+
1131+
for _, tc := range tests {
1132+
s.Run(strings.Join(tc.args, " "), func() {
1133+
getResp := namespaceservice.GetNamespaceResponse{
1134+
Namespace: &namespace.Namespace{
1135+
Namespace: ns,
1136+
Spec: &namespace.NamespaceSpec{
1137+
AcceptedClientCa: "cert1",
1138+
SearchAttributes: map[string]namespace.SearchAttributeType{
1139+
"attr1": namespace.SEARCH_ATTRIBUTE_TYPE_BOOL,
1140+
},
1141+
RetentionDays: 10,
1142+
},
1143+
State: namespace.STATE_ACTIVE,
1144+
ResourceVersion: "ver1",
1145+
},
1146+
}
1147+
if tc.expectGet != nil {
1148+
tc.expectGet(&getResp)
1149+
s.mockService.EXPECT().GetNamespace(gomock.Any(), &namespaceservice.GetNamespaceRequest{
1150+
Namespace: ns,
1151+
}).Return(&getResp, nil).Times(1)
1152+
}
1153+
1154+
if tc.expectUpdate != nil {
1155+
spec := *(getResp.Namespace.Spec)
1156+
req := namespaceservice.UpdateNamespaceRequest{
1157+
Namespace: ns,
1158+
Spec: &spec,
1159+
ResourceVersion: getResp.Namespace.ResourceVersion,
1160+
}
1161+
tc.expectUpdate(&req)
1162+
s.mockService.EXPECT().UpdateNamespace(gomock.Any(), &req).
1163+
Return(&namespaceservice.UpdateNamespaceResponse{
1164+
RequestStatus: &request.RequestStatus{},
1165+
}, nil).Times(1)
1166+
}
1167+
1168+
err := s.RunCmd(tc.args...)
1169+
if tc.expectErr {
1170+
s.Error(err)
1171+
} else {
1172+
s.NoError(err)
1173+
}
1174+
})
1175+
}
1176+
}
1177+
1178+
func (s *NamespaceTestSuite) TestGetNamespaceRetention() {
1179+
1180+
ns := "ns1"
1181+
type morphGetResp func(*namespaceservice.GetNamespaceResponse)
1182+
1183+
tests := []struct {
1184+
name string
1185+
args []string
1186+
expectGet morphGetResp
1187+
expectErr bool
1188+
}{
1189+
{
1190+
name: "displays help",
1191+
args: []string{"namespace", "retention"},
1192+
},
1193+
{
1194+
name: "missing flags",
1195+
args: []string{"namespace", "retention", "get"},
1196+
expectErr: true,
1197+
},
1198+
{
1199+
name: "happy path",
1200+
args: []string{"namespace", "retention", "get", "-namespace", ns},
1201+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {},
1202+
expectErr: false,
1203+
},
1204+
{
1205+
name: "aliases",
1206+
args: []string{"n", "r", "g", "-n", ns},
1207+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {},
1208+
expectErr: false,
1209+
},
1210+
{
1211+
name: "no namespace found",
1212+
args: []string{"namespace", "retention", "set", "-namespace", ns, "-retention-days", "7"},
1213+
expectGet: func(g *namespaceservice.GetNamespaceResponse) {
1214+
g.Namespace = nil
1215+
},
1216+
expectErr: true,
1217+
},
1218+
}
1219+
1220+
for _, tc := range tests {
1221+
s.Run(strings.Join(tc.args, " "), func() {
1222+
getResp := namespaceservice.GetNamespaceResponse{
1223+
Namespace: &namespace.Namespace{
1224+
Namespace: ns,
1225+
Spec: &namespace.NamespaceSpec{
1226+
AcceptedClientCa: "cert1",
1227+
SearchAttributes: map[string]namespace.SearchAttributeType{
1228+
"attr1": namespace.SEARCH_ATTRIBUTE_TYPE_BOOL,
1229+
},
1230+
RetentionDays: 10,
1231+
},
1232+
State: namespace.STATE_ACTIVE,
1233+
ResourceVersion: "ver1",
1234+
},
1235+
}
1236+
if tc.expectGet != nil {
1237+
tc.expectGet(&getResp)
1238+
s.mockService.EXPECT().GetNamespace(gomock.Any(), &namespaceservice.GetNamespaceRequest{
1239+
Namespace: ns,
1240+
}).Return(&getResp, nil).Times(1)
1241+
}
1242+
1243+
err := s.RunCmd(tc.args...)
1244+
if tc.expectErr {
1245+
s.Error(err)
1246+
} else {
1247+
s.NoError(err)
1248+
}
1249+
})
1250+
}
1251+
}

0 commit comments

Comments
 (0)