diff --git a/app/namespace.go b/app/namespace.go index dd5c041f..d48bdb8c 100644 --- a/app/namespace.go +++ b/app/namespace.go @@ -46,6 +46,12 @@ const ( disableFailoverFlagName = "disable-auto-failover" enableDeleteProtectionFlagName = "enable-delete-protection" tagFlagName = "tag" + + capacityModeFlagName = "capacity-mode" + capacityValueFlagName = "capacity-value" + + provisionedCapacityMode = "provisioned" + onDemandCapacityMode = "on_demand" ) const ( @@ -166,6 +172,28 @@ var ( Aliases: []string{"ids"}, Required: false, } + + capacityModeFlag = &cli.StringFlag{ + Name: capacityModeFlagName, + Usage: fmt.Sprintf("The capacity mode to use for the namespace. Valid values are '%s' and '%s'", onDemandCapacityMode, provisionedCapacityMode), + Aliases: []string{"cm"}, + Required: false, + Action: func(_ *cli.Context, s string) error { + switch s { + case "", onDemandCapacityMode, provisionedCapacityMode: + return nil + default: + return fmt.Errorf("invalid capacity mode %s, valid values are 'on_demand' and 'provisioned'", s) + } + }, + } + + capacityValueFlag = &cli.Float64Flag{ + Name: capacityValueFlagName, + Usage: "The capacity value to use for the namespace. Required if capacity mode is 'provisioned', ignored otherwise", + Aliases: []string{"cv"}, + Required: false, + } ) type NamespaceClient struct { @@ -377,6 +405,28 @@ func (c *NamespaceClient) updateNamespace(ctx *cli.Context, n *namespace.Namespa return PrintProto(res) } +func (c *NamespaceClient) updateNamespaceCloudApi(ctx *cli.Context, n *cloudNamespace.Namespace) error { + resourceVersion := n.ResourceVersion + if v := ctx.String(ResourceVersionFlagName); v != "" { + resourceVersion = v + } + + res, err := c.cloudAPIClient.UpdateNamespace(c.ctx, &cloudservice.UpdateNamespaceRequest{ + AsyncOperationId: ctx.String(RequestIDFlagName), + Namespace: n.Namespace, + ResourceVersion: resourceVersion, + Spec: n.Spec, + }) + if err != nil { + if isNothingChangedErr(ctx, err) { + return nil + } + return err + } + + return PrintProto(res) +} + func (c *NamespaceClient) updateNamespaceTags(ctx *cli.Context, tagsToUpsert map[string]string, tagsToRemove []string) error { namespace := ctx.String(NamespaceFlagName) if len(namespace) == 0 { @@ -1847,6 +1897,56 @@ func NewNamespaceCommand(getNamespaceClientFn GetNamespaceClientFn) (CommandOut, return PrintProto(n.GetCapacity()) }, }, + { + Name: "update", + Usage: "Set the capacity of a given namespace.", + Aliases: []string{"u"}, + Flags: []cli.Flag{ + NamespaceFlag, + capacityModeFlag, + capacityValueFlag, + RequestIDFlag, + ResourceVersionFlag, + }, + Action: func(ctx *cli.Context) error { + nsID := ctx.String(NamespaceFlagName) + mode := ctx.String(capacityModeFlagName) + value := ctx.Float64(capacityValueFlagName) + if mode == "" { + return fmt.Errorf("capacity mode must be specified (either '%s' or '%s')", onDemandCapacityMode, provisionedCapacityMode) + } + if mode == provisionedCapacityMode && value <= 0 { + return fmt.Errorf("capacity value must be greater than 0 when capacity mode is '%s'", provisionedCapacityMode) + } + var capacitySpec *cloudNamespace.CapacitySpec + switch mode { + case onDemandCapacityMode: + capacitySpec = &cloudNamespace.CapacitySpec{ + Spec: &cloudNamespace.CapacitySpec_OnDemand_{ + OnDemand: &cloudNamespace.CapacitySpec_OnDemand{}, + }, + } + case provisionedCapacityMode: + capacitySpec = &cloudNamespace.CapacitySpec{ + Spec: &cloudNamespace.CapacitySpec_Provisioned_{ + Provisioned: &cloudNamespace.CapacitySpec_Provisioned{ + Value: value, + }, + }, + } + } + ns, err := c.getNamespaceCloudApi(nsID) + if err != nil { + return err + } + + if ns != nil && ns.Spec == nil { + ns.Spec = &cloudNamespace.NamespaceSpec{} + } + ns.Spec.CapacitySpec = capacitySpec + return c.updateNamespaceCloudApi(ctx, ns) + }, + }, }, }, } diff --git a/app/namespace_test.go b/app/namespace_test.go index 910b30fa..7fe9be00 100644 --- a/app/namespace_test.go +++ b/app/namespace_test.go @@ -3321,3 +3321,101 @@ func (s *NamespaceTestSuite) TestGetNamespaceCapacity() { }) } } + +func (s *NamespaceTestSuite) TestUpdateNamespaceCapacity() { + tests := []struct { + name string + args []string + mock func() + expectErr bool + }{ + { + name: "update namespace capacity - success", + args: []string{"namespace", "capacity", "update", "--namespace", "ns1", "--capacity-mode", "provisioned", "--capacity-value", "16"}, + mock: func() { + s.mockCloudApiClient.EXPECT(). + GetNamespace(gomock.Any(), &cloudservice.GetNamespaceRequest{ + Namespace: "ns1", + }).Return(&cloudservice.GetNamespaceResponse{ + Namespace: &cloudNamespace.Namespace{ + Namespace: "ns1", + Capacity: &cloudNamespace.Capacity{ + CurrentMode: &cloudNamespace.Capacity_OnDemand_{ + OnDemand: &cloudNamespace.Capacity_OnDemand{}, + }, + }, + }, + }, nil).Times(1) + + s.mockCloudApiClient.EXPECT(). + UpdateNamespace(gomock.Any(), &cloudservice.UpdateNamespaceRequest{ + Namespace: "ns1", + Spec: &cloudNamespace.NamespaceSpec{ + CapacitySpec: &cloudNamespace.CapacitySpec{ + Spec: &cloudNamespace.CapacitySpec_Provisioned_{ + Provisioned: &cloudNamespace.CapacitySpec_Provisioned{ + Value: 16.0, + }, + }, + }, + }, + }). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{ + Id: "op-123", + }, + }, nil) + + }, + }, + { + name: "update namespace capacity - failure", + args: []string{"namespace", "capacity", "update", "--namespace", "ns1", "--capacity-mode", "provisioned", "--capacity-value", "16"}, + mock: func() { + s.mockCloudApiClient.EXPECT(). + GetNamespace(gomock.Any(), &cloudservice.GetNamespaceRequest{ + Namespace: "ns1", + }).Return(&cloudservice.GetNamespaceResponse{ + Namespace: &cloudNamespace.Namespace{ + Namespace: "ns1", + Capacity: &cloudNamespace.Capacity{ + CurrentMode: &cloudNamespace.Capacity_OnDemand_{ + OnDemand: &cloudNamespace.Capacity_OnDemand{}, + }, + }, + }, + }, nil).Times(1) + + s.mockCloudApiClient.EXPECT(). + UpdateNamespace(gomock.Any(), &cloudservice.UpdateNamespaceRequest{ + Namespace: "ns1", + Spec: &cloudNamespace.NamespaceSpec{ + CapacitySpec: &cloudNamespace.CapacitySpec{ + Spec: &cloudNamespace.CapacitySpec_Provisioned_{ + Provisioned: &cloudNamespace.CapacitySpec_Provisioned{ + Value: 16.0, + }, + }, + }, + }, + }). + Return(nil, errors.New("some error")) + }, + expectErr: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + if tc.mock != nil { + tc.mock() + } + err := s.RunCmd(tc.args...) + if tc.expectErr { + s.Error(err) + } else { + s.NoError(err) + } + }) + } +}