diff --git a/apiclient/apiclient.go b/apiclient/apiclient.go index bbc7fa8a..7f8e88ec 100644 --- a/apiclient/apiclient.go +++ b/apiclient/apiclient.go @@ -222,6 +222,8 @@ type Transport interface { // matching the node entity's version as seen by the server. SetCordoned(ctx context.Context, nodeID id.Node, params *SetCordonedRequestParams) error + SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *SetFailoverGracePeriodParams) error + EvictReplica(ctx context.Context, namespaceID string, id string, deploymentID string) error AttemptPromotion(ctx context.Context, namespaceID string, id string, deploymentID string) error diff --git a/apiclient/mock_transport_test.go b/apiclient/mock_transport_test.go index 5d62c4f9..c7961750 100644 --- a/apiclient/mock_transport_test.go +++ b/apiclient/mock_transport_test.go @@ -431,6 +431,10 @@ func (m *mockTransport) SetCordoned(ctx context.Context, nodeID id.Node, params return m.SetCordonedErr } +func (m *mockTransport) SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *SetFailoverGracePeriodParams) error { + panic("not implemented") +} + func (m *mockTransport) EvictReplica(ctx context.Context, namespaceID string, id string, deploymentID string) error { return nil } diff --git a/apiclient/no_transport.go b/apiclient/no_transport.go index 28b04c6e..dcceda5d 100644 --- a/apiclient/no_transport.go +++ b/apiclient/no_transport.go @@ -169,6 +169,10 @@ func (t *noTransport) SetCordoned(ctx context.Context, nodeID id.Node, params *S return ErrNoTransportConfigured } +func (t *noTransport) SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *SetFailoverGracePeriodParams) error { + return ErrNoTransportConfigured +} + func (t *noTransport) EvictReplica(ctx context.Context, namespaceID string, id string, deploymentID string) error { return ErrNoTransportConfigured } diff --git a/apiclient/node.go b/apiclient/node.go index 3d73ea22..0792bf1d 100644 --- a/apiclient/node.go +++ b/apiclient/node.go @@ -24,6 +24,13 @@ type SetCordonedRequestParams struct { CASVersion version.Version } +// SetFailoverGracePeriodParams contains the required and optional parameteres +// for a set failover grace period operation +type SetFailoverGracePeriodParams struct { + GracePeriod uint64 + CASVersion version.Version +} + // NodeNotFoundError indicates that the API could not find the StorageOS node // specified. type NodeNotFoundError struct { diff --git a/apiclient/openapi/codec.go b/apiclient/openapi/codec.go index fd844f28..532c0031 100644 --- a/apiclient/openapi/codec.go +++ b/apiclient/openapi/codec.go @@ -2,6 +2,7 @@ package openapi import ( "errors" + "time" openapi "github.com/storageos/go-api/autogenerated" @@ -73,8 +74,9 @@ func (c codec) decodeNode(node openapi.Node) (*model.Node, error) { GossipAddr: node.GossipEndpoint, ClusteringAddr: node.ClusteringEndpoint, - Cordoned: node.Cordoned, - CordonedAt: node.CordonedAt, + Cordoned: node.Cordoned, + CordonedAt: node.CordonedAt, + FailoverGracePeriod: time.Millisecond * time.Duration(node.FailoverGracePeriod), CreatedAt: node.CreatedAt, UpdatedAt: node.UpdatedAt, diff --git a/apiclient/openapi/node.go b/apiclient/openapi/node.go index 383c7750..a6f8228d 100644 --- a/apiclient/openapi/node.go +++ b/apiclient/openapi/node.go @@ -59,18 +59,18 @@ func (o *OpenAPI) ListNodes(ctx context.Context) ([]*model.Node, error) { // // The behaviour of the operation is dictated by params: // -// Version constraints: -// - If params is nil or params.CASVersion is empty then the delete request is -// unconditional -// - If params.CASVersion is set, the request is conditional upon it matching -// the node entity's version as seen by the server. +// Version constraints: +// - If params is nil or params.CASVersion is empty then the delete request is +// unconditional +// - If params.CASVersion is set, the request is conditional upon it matching +// the node entity's version as seen by the server. // -// Asynchrony: -// - If params is nil or params.AsyncMax is empty/zero valued then the delete -// request is performed synchronously. -// - If params.AsyncMax is set, the request is performed asynchronously using -// the duration given as the maximum amount of time allowed for the request -// before it times out. +// Asynchrony: +// - If params is nil or params.AsyncMax is empty/zero valued then the delete +// request is performed synchronously. +// - If params.AsyncMax is set, the request is performed asynchronously using +// the duration given as the maximum amount of time allowed for the request +// before it times out. func (o *OpenAPI) DeleteNode(ctx context.Context, nodeID id.Node, params *apiclient.DeleteNodeRequestParams) error { o.mu.RLock() defer o.mu.RUnlock() @@ -115,15 +115,15 @@ func (o *OpenAPI) DeleteNode(ctx context.Context, nodeID id.Node, params *apicli // // The behaviour of the operation is dictated by params: // -// Version constraints: -// - If params is nil or params.CASVersion is empty then the delete request is -// unconditional -// - If params.CASVersion is set, the request is conditional upon it matching -// the node entity's version as seen by the server. +// Version constraints: +// - If params is nil or params.CASVersion is empty then the delete request is +// unconditional +// - If params.CASVersion is set, the request is conditional upon it matching +// the node entity's version as seen by the server. // -// Cordoned: -// - If true marks the node as cordoned -// - If false marks the node as not cordoned +// Cordoned: +// - If true marks the node as cordoned +// - If false marks the node as not cordoned func (o *OpenAPI) SetCordoned(ctx context.Context, nodeID id.Node, params *apiclient.SetCordonedRequestParams) error { o.mu.RLock() defer o.mu.RUnlock() @@ -160,3 +160,41 @@ func (o *OpenAPI) SetCordoned(ctx context.Context, nodeID id.Node, params *apicl return nil } + +// SetFailoverGracePeriod calls the SetFailoverGracePeriod REST endpoint +func (o *OpenAPI) SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *apiclient.SetFailoverGracePeriodParams) error { + o.mu.RLock() + defer o.mu.RUnlock() + + var casVersion string + var ignoreVersion optional.Bool = optional.NewBool(true) + + if params != nil { + if params.CASVersion.String() != "" { + ignoreVersion = optional.NewBool(false) + casVersion = params.CASVersion.String() + } + } + _, resp, err := o.client.DefaultApi.SetFailoverGracePeriod( + ctx, + nodeID.String(), + openapi.SetFailoverGracePeriodData{ + GracePeriod: params.GracePeriod, + Version: casVersion, + }, + &openapi.SetFailoverGracePeriodOpts{ + IgnoreVersion: ignoreVersion, + }, + ) + + if err != nil { + switch v := mapOpenAPIError(err, resp).(type) { + case notFoundError: + return apiclient.NewNodeNotFoundError(nodeID) + default: + return v + } + } + + return nil +} diff --git a/apiclient/reauth_transport.go b/apiclient/reauth_transport.go index ec143683..ed2248a9 100644 --- a/apiclient/reauth_transport.go +++ b/apiclient/reauth_transport.go @@ -522,6 +522,15 @@ func (tr *TransportWithReauth) SetCordoned(ctx context.Context, nodeID id.Node, return err } +func (tr *TransportWithReauth) SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *SetFailoverGracePeriodParams) error { + + err := tr.doWithReauth(ctx, func() error { + return tr.inner.SetFailoverGracePeriod(ctx, nodeID, params) + }) + + return err +} + func (tr *TransportWithReauth) EvictReplica(ctx context.Context, namespaceID string, id string, deploymentID string) error { return tr.doWithReauth(ctx, func() error { return tr.inner.EvictReplica(ctx, namespaceID, id, deploymentID) @@ -569,10 +578,10 @@ func (tr *TransportWithReauth) SetPreferredEvictionCandidates(ctx context.Contex // doWithReauth invokes fn, checking the resultant error. // -// - If the error is an *AuthenticationError then tr's credentials are -// used to reauthenticate before returning the result from re-invoking fn. -// If any errors occur during reauthentication, they are returned. -// - Otherwise, the original error is returned to the caller. +// - If the error is an *AuthenticationError then tr's credentials are +// used to reauthenticate before returning the result from re-invoking fn. +// If any errors occur during reauthentication, they are returned. +// - Otherwise, the original error is returned to the caller. func (tr *TransportWithReauth) doWithReauth(ctx context.Context, fn func() error) error { originalErr := fn() diff --git a/cmd/interfaces/interfaces.go b/cmd/interfaces/interfaces.go index eecc9052..3ec759b2 100644 --- a/cmd/interfaces/interfaces.go +++ b/cmd/interfaces/interfaces.go @@ -73,6 +73,7 @@ type Client interface { DeletePolicyGroup(ctx context.Context, uid id.PolicyGroup, params *apiclient.DeletePolicyGroupRequestParams) error SetCordoned(ctx context.Context, nodeID id.Node, params *apiclient.SetCordonedRequestParams) error + SetFailoverGracePeriod(ctx context.Context, nodeID id.Node, params *apiclient.SetFailoverGracePeriodParams) error UpdateLicence(ctx context.Context, licenceKey []byte, params *apiclient.UpdateLicenceRequestParams) (*model.License, error) AttachVolume(ctx context.Context, namespaceID id.Namespace, volumeID id.Volume, nodeID id.Node) error DetachVolume(ctx context.Context, namespaceID id.Namespace, volumeID id.Volume, params *apiclient.DetachVolumeRequestParams) error diff --git a/cmd/update/maintenance_mode.go b/cmd/update/maintenance_mode.go new file mode 100644 index 00000000..99ac2c57 --- /dev/null +++ b/cmd/update/maintenance_mode.go @@ -0,0 +1,100 @@ +package update + +import ( + "context" + "errors" + "fmt" + "io" + "strconv" + "time" + + "code.storageos.net/storageos/c2-cli/apiclient" + "code.storageos.net/storageos/c2-cli/cmd/argwrappers" + "code.storageos.net/storageos/c2-cli/cmd/interfaces" + "code.storageos.net/storageos/c2-cli/cmd/runwrappers" + "github.com/spf13/cobra" +) + +type maintenanceModeCommand struct { + config interfaces.ConfigProvider + client interfaces.Client + w io.Writer +} + +// NewMaintenanceModeCmd defines config for the "maintenance-mode" command +func NewMaintenanceModeCmd(w io.Writer, client interfaces.Client, config interfaces.ConfigProvider) *cobra.Command { + c := &maintenanceModeCommand{ + config: config, + client: client, + w: w, + } + + cmd := &cobra.Command{ + Use: "maintenance-mode ", + Short: "Sets maintenance mode for the entire cluster", + Long: `Sets maintenance mode for the entire cluster +When maintenance mode is enabled failovers will not occur`, + Example: " storageos maintenance-mode true", + Args: argwrappers.WrapInvalidArgsError(func(_ *cobra.Command, args []string) error { + if len(args) != 1 { + return errors.New("must specify true or false") + } + return nil + }), + RunE: func(cmd *cobra.Command, args []string) error { + run := runwrappers.Chain( + runwrappers.RunWithTimeout(config), + runwrappers.AuthenticateClient(config, client), + )(c.run) + + return run(context.Background(), cmd, args) + }, + } + + return cmd +} + +func (c *maintenanceModeCommand) run(ctx context.Context, _ *cobra.Command, args []string) error { + useIDs, err := c.config.UseIDs() + if err != nil { + return err + } + + enabled, err := strconv.ParseBool(args[0]) + if err != nil { + return fmt.Errorf("failed to parse boolean from arguments: %w", err) + } + + nodes, err := c.client.ListNodes(ctx) + if err != nil { + return fmt.Errorf("failed to list nodes: %w", err) + } + + var gracePeriod uint64 + if enabled { + // Set the failover period to a 100 years, which should be a long enough maintenance window + gracePeriod = uint64(time.Hour.Milliseconds() * 24 * 365 * 100) + } + + for _, node := range nodes { + + err = c.client.SetFailoverGracePeriod(ctx, node.ID, &apiclient.SetFailoverGracePeriodParams{ + GracePeriod: gracePeriod, + }) + if err != nil { + n := node.Name + if useIDs { + n = string(node.ID) + } + + return fmt.Errorf("failed to set maintenance mode for node %v: %w", n, err) + } + + nodeOutput := node.Name + if useIDs { + nodeOutput = string(node.ID) + } + fmt.Fprintf(c.w, "set maintenance mode to %v for node/%s\n", enabled, nodeOutput) + } + return nil +} diff --git a/cmd/update/node.go b/cmd/update/node.go new file mode 100644 index 00000000..110c56ab --- /dev/null +++ b/cmd/update/node.go @@ -0,0 +1,22 @@ +package update + +import ( + "os" + + "code.storageos.net/storageos/c2-cli/cmd/interfaces" + "github.com/spf13/cobra" +) + +// newNodeUpdate configures the set of commands which are grouped by the +// "node" noun. +func newNodeUpdate(client interfaces.Client, config interfaces.ConfigProvider) *cobra.Command { + command := &cobra.Command{ + Use: "node", + Short: "Make changes to a node", + } + + command.AddCommand(NewFailoverGracePeriodCmd(os.Stdout, client, config)) + command.AddCommand(NewMaintenanceModeCmd(os.Stdout, client, config)) + + return command +} diff --git a/cmd/update/node_failover_grace_period.go b/cmd/update/node_failover_grace_period.go new file mode 100644 index 00000000..a1ec8f70 --- /dev/null +++ b/cmd/update/node_failover_grace_period.go @@ -0,0 +1,107 @@ +package update + +import ( + "context" + "errors" + "fmt" + "io" + "time" + + "code.storageos.net/storageos/c2-cli/apiclient" + "code.storageos.net/storageos/c2-cli/cmd/argwrappers" + "code.storageos.net/storageos/c2-cli/cmd/flagutil" + "code.storageos.net/storageos/c2-cli/cmd/interfaces" + "code.storageos.net/storageos/c2-cli/cmd/runwrappers" + "code.storageos.net/storageos/c2-cli/pkg/id" + "code.storageos.net/storageos/c2-cli/pkg/version" + "github.com/spf13/cobra" +) + +type failoverGracePeriodCommand struct { + config interfaces.ConfigProvider + client interfaces.Client + w io.Writer + + casVersion string + useCAS func() bool +} + +// NewFailoverGracePeriodCmd defines config for the "failover-grace-period" command +func NewFailoverGracePeriodCmd(w io.Writer, client interfaces.Client, config interfaces.ConfigProvider) *cobra.Command { + c := &failoverGracePeriodCommand{ + config: config, + client: client, + w: w, + } + + cmd := &cobra.Command{ + Use: "failover-grace-period [node] [failover grace period]", + Short: "Sets the failover grace period for a node", + Long: `Sets the failover grace period for a node +This defines the amount of time the node will wait before determining other nodes are offline, and therefore failing-over volumes.`, + Example: " storageos failover-grace-period my-node-name 30000ms", + Args: argwrappers.WrapInvalidArgsError(func(_ *cobra.Command, args []string) error { + if len(args) != 2 { + return errors.New("must specify exactly one node and the failover grace period") + } + return nil + }), + RunE: func(cmd *cobra.Command, args []string) error { + run := runwrappers.Chain( + runwrappers.RunWithTimeout(config), + runwrappers.AuthenticateClient(config, client), + )(c.run) + + return run(context.Background(), cmd, args) + }, + } + + c.useCAS = flagutil.SupportCAS(cmd.Flags(), &c.casVersion) + + return cmd +} + +func (c *failoverGracePeriodCommand) run(ctx context.Context, _ *cobra.Command, args []string) error { + useIDs, err := c.config.UseIDs() + if err != nil { + return err + } + + var params apiclient.SetFailoverGracePeriodParams + + if c.useCAS() { + params.CASVersion = version.FromString(c.casVersion) + } + + nodeID := id.Node(args[0]) + if !useIDs { + nodeName := args[0] + n, err := c.client.GetNodeByName(ctx, nodeName) + if err != nil { + return err + } + nodeID = n.ID + } + + gracePeriod, err := time.ParseDuration(args[1]) + if err != nil { + return fmt.Errorf("failed to parse failover grace period from arguments: %w", err) + } + + params.GracePeriod = uint64(gracePeriod.Milliseconds()) + + err = c.client.SetFailoverGracePeriod(ctx, nodeID, ¶ms) + if err != nil { + return err + } + + // Print either the ID or the node name depending on which the user supplied + nodeOutput := string(nodeID) + if !useIDs { + nodeOutput = args[0] + } + + fmt.Fprintf(c.w, "updated node/%s's failover grace period to %v\n", nodeOutput, gracePeriod) + + return nil +} diff --git a/cmd/update/update.go b/cmd/update/update.go index 594ae6ad..170dc328 100644 --- a/cmd/update/update.go +++ b/cmd/update/update.go @@ -19,6 +19,7 @@ func NewCommand(client interfaces.Client, config interfaces.ConfigProvider) *cob command.AddCommand( newVolumeUpdate(client, config), + newNodeUpdate(client, config), ) return command diff --git a/go.mod b/go.mod index 48846c37..4feadbf3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/kr/pretty v0.1.0 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.3 - github.com/storageos/go-api v0.0.0-20221110132555-4ecf9f9b9429 + github.com/storageos/go-api v0.0.0-20230214110526-c9ac2f1afe98 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 golang.org/x/sync v0.0.0-20190423024810-112230192c58 diff --git a/go.sum b/go.sum index 5bd258a4..34378f22 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,12 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/storageos/go-api v0.0.0-20221110132555-4ecf9f9b9429 h1:dwZKmnSDyBhp0jI7HCGCy5HJ22ilNKwWh0HP1ybMwJ0= github.com/storageos/go-api v0.0.0-20221110132555-4ecf9f9b9429/go.mod h1:8nTfqI+/wEmSs+gk/U1ZljZ+GnduPqi0HwIVXep9pjk= +github.com/storageos/go-api v0.0.0-20230214103457-e26660ed8fab h1:xuVHC7hJ0qUbSdjKr2Hb0CzvqXIgjplXSx/y94wcbx0= +github.com/storageos/go-api v0.0.0-20230214103457-e26660ed8fab/go.mod h1:8nTfqI+/wEmSs+gk/U1ZljZ+GnduPqi0HwIVXep9pjk= +github.com/storageos/go-api v0.0.0-20230214103930-bc3aa5006ab9 h1:C6b4hlllsl/KH2RcdYqAsCYrWCrse/bDmQCqcuwP91I= +github.com/storageos/go-api v0.0.0-20230214103930-bc3aa5006ab9/go.mod h1:8nTfqI+/wEmSs+gk/U1ZljZ+GnduPqi0HwIVXep9pjk= +github.com/storageos/go-api v0.0.0-20230214110526-c9ac2f1afe98 h1:u/j0QcHwGR4QqlW1UvwMKLXJ2WFCQvGnJTmKHVwE4sU= +github.com/storageos/go-api v0.0.0-20230214110526-c9ac2f1afe98/go.mod h1:8nTfqI+/wEmSs+gk/U1ZljZ+GnduPqi0HwIVXep9pjk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/model/node.go b/model/node.go index b863091a..0f8769e7 100644 --- a/model/node.go +++ b/model/node.go @@ -22,8 +22,9 @@ type Node struct { GossipAddr string `json:"gossipAddress"` ClusteringAddr string `json:"clusteringAddress"` - Cordoned bool `json:"cordoned"` - CordonedAt time.Time `json:"cordonedAt"` + Cordoned bool `json:"cordoned"` + CordonedAt time.Time `json:"cordonedAt"` + FailoverGracePeriod time.Duration `json:"failoverGracePeriod"` Labels labels.Set `json:"labels"` diff --git a/output/node.go b/output/node.go index 70adb6b5..0181523d 100644 --- a/output/node.go +++ b/output/node.go @@ -13,20 +13,21 @@ import ( // Node defines a type that contains all the info we need to output a node. type Node struct { - ID id.Node `json:"id" yaml:"id"` - Name string `json:"name" yaml:"name"` - Health health.NodeState `json:"health" yaml:"health"` - Capacity capacity.Stats `json:"capacity,omitempty" yaml:"capacity,omitempty"` - IOAddr string `json:"ioAddress" yaml:"ioAddress"` - SupervisorAddr string `json:"supervisorAddress" yaml:"supervisorAddress"` - GossipAddr string `json:"gossipAddress" yaml:"gossipAddr"` - ClusteringAddr string `json:"clusteringAddress" yaml:"clusteringAddr"` - Labels labels.Set `json:"labels" yaml:"labels"` - Cordoned bool `json:"cordoned" yaml:"cordoned"` - CordonedAt time.Time `json:"cordonedAt" yaml:"cordonedAt"` - CreatedAt time.Time `json:"createdAt" yaml:"createdAt"` - UpdatedAt time.Time `json:"updatedAt" yaml:"updatedAt"` - Version version.Version `json:"version" yaml:"version"` + ID id.Node `json:"id" yaml:"id"` + Name string `json:"name" yaml:"name"` + Health health.NodeState `json:"health" yaml:"health"` + Capacity capacity.Stats `json:"capacity,omitempty" yaml:"capacity,omitempty"` + IOAddr string `json:"ioAddress" yaml:"ioAddress"` + SupervisorAddr string `json:"supervisorAddress" yaml:"supervisorAddress"` + GossipAddr string `json:"gossipAddress" yaml:"gossipAddr"` + ClusteringAddr string `json:"clusteringAddress" yaml:"clusteringAddr"` + Labels labels.Set `json:"labels" yaml:"labels"` + Cordoned bool `json:"cordoned" yaml:"cordoned"` + CordonedAt time.Time `json:"cordonedAt" yaml:"cordonedAt"` + FailoverGracePeriod time.Duration `json:"failoverGracePeriod" yaml:"failoverGracePeriod"` + CreatedAt time.Time `json:"createdAt" yaml:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" yaml:"updatedAt"` + Version version.Version `json:"version" yaml:"version"` } // NodeDescription decorates a Node's output representation with additional @@ -71,20 +72,21 @@ type NodeDeletion struct { // be outputted. func NewNode(n *model.Node) *Node { return &Node{ - ID: n.ID, - Name: n.Name, - Health: n.Health, - Capacity: n.Capacity, - IOAddr: n.IOAddr, - SupervisorAddr: n.SupervisorAddr, - GossipAddr: n.GossipAddr, - ClusteringAddr: n.ClusteringAddr, - Cordoned: n.Cordoned, - CordonedAt: n.CordonedAt, - Labels: n.Labels, - CreatedAt: n.CreatedAt, - UpdatedAt: n.UpdatedAt, - Version: n.Version, + ID: n.ID, + Name: n.Name, + Health: n.Health, + Capacity: n.Capacity, + IOAddr: n.IOAddr, + SupervisorAddr: n.SupervisorAddr, + GossipAddr: n.GossipAddr, + ClusteringAddr: n.ClusteringAddr, + Cordoned: n.Cordoned, + CordonedAt: n.CordonedAt, + FailoverGracePeriod: n.FailoverGracePeriod, + Labels: n.Labels, + CreatedAt: n.CreatedAt, + UpdatedAt: n.UpdatedAt, + Version: n.Version, } } diff --git a/output/textformat/describe.go b/output/textformat/describe.go index 743cfbe4..732015f3 100644 --- a/output/textformat/describe.go +++ b/output/textformat/describe.go @@ -142,6 +142,7 @@ func (d *Displayer) describeNode(ctx context.Context, w io.Writer, node *output. if node.Cordoned { table.AddRow("Cordoned at", d.timeToHuman(node.CordonedAt)) } + table.AddRow("Failover Grace Period", node.FailoverGracePeriod.String()) table.AddRow("Created at", d.timeToHuman(node.CreatedAt)) table.AddRow("Updated at", d.timeToHuman(node.UpdatedAt)) table.AddRow("Version", node.Version.String()) diff --git a/output/textformat/describe_test.go b/output/textformat/describe_test.go index 6cd54a8f..3513e6df 100644 --- a/output/textformat/describe_test.go +++ b/output/textformat/describe_test.go @@ -57,11 +57,12 @@ func TestDisplayer_DescribeNode(t *testing.T) { "a": "b", "1": "2", }, - Cordoned: true, - CordonedAt: cordonedTime, - CreatedAt: createdTime, - UpdatedAt: updatedTime, - Version: "some-version", + Cordoned: true, + CordonedAt: cordonedTime, + FailoverGracePeriod: 10 * time.Millisecond, + CreatedAt: createdTime, + UpdatedAt: updatedTime, + Version: "some-version", }, HostedVolumes: []*output.HostedVolume{ { @@ -117,6 +118,7 @@ Labels 1=2, a=b Cordoned true Cordoned at 2000-02-02T03:00:00Z (a long time ago) +Failover Grace Period 10ms Created at 2000-01-01T00:00:00Z (a long time ago) Updated at 2001-01-01T00:00:00Z (a long time ago) Version some-version @@ -168,6 +170,7 @@ Addresses: Labels 1=2, a=b Cordoned false +Failover Grace Period 0s Created at 2000-01-01T00:00:00Z (a long time ago) Updated at 2001-01-01T00:00:00Z (a long time ago) Version some-version diff --git a/vendor/github.com/storageos/go-api/autogenerated/README.md b/vendor/github.com/storageos/go-api/autogenerated/README.md index 03deb7c1..f4933cf6 100644 --- a/vendor/github.com/storageos/go-api/autogenerated/README.md +++ b/vendor/github.com/storageos/go-api/autogenerated/README.md @@ -6,7 +6,7 @@ No description provided (generated by Openapi Generator https://github.com/opena This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. - API version: 2.8.0 -- Package version: 2.9.0 +- Package version: 1.0.0 - Build package: org.openapitools.codegen.languages.GoClientCodegen For more information, please visit [https://www.ondat.io/](https://www.ondat.io/) @@ -75,6 +75,7 @@ Class | Method | HTTP request | Description *DefaultApi* | [**ResizeVolume**](docs/DefaultApi.md#resizevolume) | **Put** /namespaces/{namespaceID}/volumes/{id}/size | Increase the size of a volume. *DefaultApi* | [**SetComputeOnly**](docs/DefaultApi.md#setcomputeonly) | **Put** /nodes/{id}/compute-only | Modify the computeonly behaviour state for a node *DefaultApi* | [**SetCordoned**](docs/DefaultApi.md#setcordoned) | **Put** /nodes/{id}/cordon | Modify the cordoned state for a node +*DefaultApi* | [**SetFailoverGracePeriod**](docs/DefaultApi.md#setfailovergraceperiod) | **Put** /nodes/{id}/failover-grace-period | Modify the failover grace period for a node *DefaultApi* | [**SetFailureMode**](docs/DefaultApi.md#setfailuremode) | **Put** /namespaces/{namespaceID}/volumes/{id}/failure-mode | Set the failure mode of the volume. *DefaultApi* | [**SetPlacementStrategy**](docs/DefaultApi.md#setplacementstrategy) | **Put** /namespaces/{namespaceID}/volumes/{id}/placement-strategy | Sets the placement strategy of the volume. *DefaultApi* | [**SetPreferredEvictionCandidates**](docs/DefaultApi.md#setpreferredevictioncandidates) | **Put** /namespaces/{namespaceID}/volumes/{id}/preferred-eviction-candidates | Specifies a list of deployments to be preferred to be evicted. @@ -140,6 +141,7 @@ Class | Method | HTTP request | Description - [ResizeVolumeRequest](docs/ResizeVolumeRequest.md) - [SetComputeOnlyNodeData](docs/SetComputeOnlyNodeData.md) - [SetCordonedNodeData](docs/SetCordonedNodeData.md) + - [SetFailoverGracePeriodData](docs/SetFailoverGracePeriodData.md) - [SetFailureModeIntentRequestData](docs/SetFailureModeIntentRequestData.md) - [SetFailureModeRequest](docs/SetFailureModeRequest.md) - [SetFailureThresholdRequestData](docs/SetFailureThresholdRequestData.md) diff --git a/vendor/github.com/storageos/go-api/autogenerated/api_default.go b/vendor/github.com/storageos/go-api/autogenerated/api_default.go index 673db38b..a80f6016 100644 --- a/vendor/github.com/storageos/go-api/autogenerated/api_default.go +++ b/vendor/github.com/storageos/go-api/autogenerated/api_default.go @@ -5924,6 +5924,166 @@ func (a *DefaultApiService) SetCordoned(ctx _context.Context, id string, setCord return localVarReturnValue, localVarHTTPResponse, nil } +// SetFailoverGracePeriodOpts Optional parameters for the method 'SetFailoverGracePeriod' +type SetFailoverGracePeriodOpts struct { + IgnoreVersion optional.Bool +} + +/* +SetFailoverGracePeriod Modify the failover grace period for a node +Set the failover grace period for the node corresponding to id given by the request. + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param id ID of a Node + * @param setFailoverGracePeriodData + * @param optional nil or *SetFailoverGracePeriodOpts - Optional Parameters: + * @param "IgnoreVersion" (optional.Bool) - If set to true this value indicates that the user wants to ignore entity version constraints, thereby \"forcing\" the operation. +@return Node +*/ +func (a *DefaultApiService) SetFailoverGracePeriod(ctx _context.Context, id string, setFailoverGracePeriodData SetFailoverGracePeriodData, localVarOptionals *SetFailoverGracePeriodOpts) (Node, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPut + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue Node + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/nodes/{id}/failover-grace-period" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", _neturl.QueryEscape(parameterToString(id, "")) , -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if localVarOptionals != nil && localVarOptionals.IgnoreVersion.IsSet() { + localVarQueryParams.Add("ignore-version", parameterToString(localVarOptionals.IgnoreVersion.Value(), "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = &setFailoverGracePeriodData + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 403 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 412 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 503 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + // SetFailureModeOpts Optional parameters for the method 'SetFailureMode' type SetFailureModeOpts struct { IgnoreVersion optional.Bool diff --git a/vendor/github.com/storageos/go-api/autogenerated/configuration.go b/vendor/github.com/storageos/go-api/autogenerated/configuration.go index 7c1723b0..d078cd42 100644 --- a/vendor/github.com/storageos/go-api/autogenerated/configuration.go +++ b/vendor/github.com/storageos/go-api/autogenerated/configuration.go @@ -84,7 +84,7 @@ func NewConfiguration() *Configuration { cfg := &Configuration{ BasePath: "http://localhost/v2", DefaultHeader: make(map[string]string), - UserAgent: "OpenAPI-Generator/2.9.0/go", + UserAgent: "OpenAPI-Generator/1.0.0/go", Debug: false, Servers: []ServerConfiguration{ { diff --git a/vendor/github.com/storageos/go-api/autogenerated/model_node.go b/vendor/github.com/storageos/go-api/autogenerated/model_node.go index 1a5be253..08483f92 100644 --- a/vendor/github.com/storageos/go-api/autogenerated/model_node.go +++ b/vendor/github.com/storageos/go-api/autogenerated/model_node.go @@ -8,35 +8,39 @@ */ package api + import ( "time" ) + // Node struct for Node type Node struct { - // A unique identifier for a node. The format of this type is undefined and may change but the defined properties will not change. + // A unique identifier for a node. The format of this type is undefined and may change but the defined properties will not change. Id string `json:"id,omitempty"` - // The hostname of the node. This value is set by the node each time it joins the Ondat cluster. - Name string `json:"name,omitempty"` - Health NodeHealth `json:"health,omitempty"` + // The hostname of the node. This value is set by the node each time it joins the Ondat cluster. + Name string `json:"name,omitempty"` + Health NodeHealth `json:"health,omitempty"` Capacity CapacityStats `json:"capacity,omitempty"` - // Endpoint at which we operate our dataplane's dfs service. (used for IO operations) This value is set on startup by the corresponding environment variable (IO_ADVERTISE_ADDRESS) + // Endpoint at which we operate our dataplane's dfs service. (used for IO operations) This value is set on startup by the corresponding environment variable (IO_ADVERTISE_ADDRESS) IoEndpoint string `json:"ioEndpoint,omitempty"` - // Endpoint at which we operate our dataplane's supervisor service (used for sync). This value is set on startup by the corresponding environment variable (SUPERVISOR_ADVERTISE_ADDRESS) + // Endpoint at which we operate our dataplane's supervisor service (used for sync). This value is set on startup by the corresponding environment variable (SUPERVISOR_ADVERTISE_ADDRESS) SupervisorEndpoint string `json:"supervisorEndpoint,omitempty"` - // Endpoint at which we operate our health checking service. This value is set on startup by the corresponding environment variable (GOSSIP_ADVERTISE_ADDRESS) + // Endpoint at which we operate our health checking service. This value is set on startup by the corresponding environment variable (GOSSIP_ADVERTISE_ADDRESS) GossipEndpoint string `json:"gossipEndpoint,omitempty"` - // Endpoint at which we operate our clustering gRPC API. This value is set on startup by the corresponding environment variable (INTERNAL_API_ADVERTISE_ADDRESS) + // Endpoint at which we operate our clustering gRPC API. This value is set on startup by the corresponding environment variable (INTERNAL_API_ADVERTISE_ADDRESS) ClusteringEndpoint string `json:"clusteringEndpoint,omitempty"` - // Cordoned describes the cordoned state of the node. A cordoned node will not have new volume deployments scheduled on it + // Cordoned describes the cordoned state of the node. A cordoned node will not have new volume deployments scheduled on it Cordoned bool `json:"cordoned,omitempty"` - // The time the node has been cordoned. This field's purpose is informative only. String format is RFC3339. + // The time the node has been cordoned. This field's purpose is informative only. String format is RFC3339. CordonedAt time.Time `json:"cordonedAt,omitempty"` - // A set of arbitrary key value labels to apply to the entity. + // The failover grace period of the node in milliseconds. + FailoverGracePeriod uint64 `json:"failoverGracePeriod,omitempty"` + // A set of arbitrary key value labels to apply to the entity. Labels map[string]string `json:"labels,omitempty"` - // The time the entity was created. This timestamp is set by the node that created the entity, and may not be correct if the node's local clock was skewed. This value is for the user's informative purposes only, and correctness is not required. String format is RFC3339. + // The time the entity was created. This timestamp is set by the node that created the entity, and may not be correct if the node's local clock was skewed. This value is for the user's informative purposes only, and correctness is not required. String format is RFC3339. CreatedAt time.Time `json:"createdAt,omitempty"` - // The time the entity was last updated. This timestamp is set by the node that last updated the entity, and may not be correct if the node's local clock was skewed. This value is for the user's informative purposes only, and correctness is not required. String format is RFC3339. + // The time the entity was last updated. This timestamp is set by the node that last updated the entity, and may not be correct if the node's local clock was skewed. This value is for the user's informative purposes only, and correctness is not required. String format is RFC3339. UpdatedAt time.Time `json:"updatedAt,omitempty"` - // An opaque representation of an entity version at the time it was obtained from the API. All operations that mutate the entity must include this version field in the request unchanged. The format of this type is undefined and may change but the defined properties will not change. + // An opaque representation of an entity version at the time it was obtained from the API. All operations that mutate the entity must include this version field in the request unchanged. The format of this type is undefined and may change but the defined properties will not change. Version string `json:"version,omitempty"` } diff --git a/vendor/github.com/storageos/go-api/autogenerated/model_set_failover_grace_period_data.go b/vendor/github.com/storageos/go-api/autogenerated/model_set_failover_grace_period_data.go new file mode 100644 index 00000000..f39a5200 --- /dev/null +++ b/vendor/github.com/storageos/go-api/autogenerated/model_set_failover_grace_period_data.go @@ -0,0 +1,17 @@ +/* + * Ondat API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * API version: 2.8.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package api +// SetFailoverGracePeriodData struct for SetFailoverGracePeriodData +type SetFailoverGracePeriodData struct { + // The node's desired failover grace period in milliseconds. + GracePeriod uint64 `json:"gracePeriod,omitempty"` + // An opaque representation of an entity version at the time it was obtained from the API. All operations that mutate the entity must include this version field in the request unchanged. The format of this type is undefined and may change but the defined properties will not change. + Version string `json:"version,omitempty"` +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 837bd435..c3ff71c8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -58,7 +58,7 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.3 ## explicit github.com/spf13/pflag -# github.com/storageos/go-api v0.0.0-20221110132555-4ecf9f9b9429 +# github.com/storageos/go-api v0.0.0-20230214110526-c9ac2f1afe98 ## explicit; go 1.17 github.com/storageos/go-api/autogenerated # github.com/stretchr/testify v1.7.0