diff --git a/reaper/client.go b/reaper/client.go index da9fdf5..386b64c 100644 --- a/reaper/client.go +++ b/reaper/client.go @@ -11,6 +11,7 @@ import ( "net/url" "runtime" "sync" + "time" ) type ReaperClient interface { @@ -31,6 +32,10 @@ type ReaperClient interface { AddCluster(ctx context.Context, cluster string, seed string) error DeleteCluster(ctx context.Context, cluster string) error + + RepairSchedules(ctx context.Context) ([]RepairSchedule, error) + + RepairSchedulesForCluster(ctx context.Context, clusterName string) ([]RepairSchedule, error) } type Client struct { @@ -43,7 +48,7 @@ func newClient(reaperBaseURL string) (*Client, error) { if baseURL, err := url.Parse(reaperBaseURL); err != nil { return nil, err } else { - return &Client{BaseURL: baseURL, UserAgent: "", httpClient: &http.Client{}}, nil + return &Client{BaseURL: baseURL, UserAgent: "", httpClient: &http.Client{Timeout: 3 * time.Second}}, nil } } @@ -176,7 +181,7 @@ func (c *Client) AddCluster(ctx context.Context, cluster string, seed string) er resp, err := c.httpClient.Do(req) if err != nil { select { - case <- ctx.Done(): + case <-ctx.Done(): return ctx.Err() default: } @@ -217,6 +222,36 @@ func (c *Client) DeleteCluster(ctx context.Context, cluster string) error { return nil } +func (c *Client) RepairSchedules(ctx context.Context) ([]RepairSchedule, error) { + rel := &url.URL{Path: "/repair_schedule"} + return c.fetchRepairSchedules(ctx, rel) +} + +func (c *Client) RepairSchedulesForCluster(ctx context.Context, clusterName string) ([]RepairSchedule, error) { + rel := &url.URL{Path: fmt.Sprintf("/repair_schedule/cluster/%s", clusterName)} + return c.fetchRepairSchedules(ctx, rel) +} + +func (c *Client) fetchRepairSchedules(ctx context.Context, rel *url.URL) ([]RepairSchedule, error) { + u := c.BaseURL.ResolveReference(rel) + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + + if err != nil { + return nil, err + } + + schedules := make([]RepairSchedule, 0) + resp, err := c.doJsonRequest(ctx, req, &schedules) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("Failed to fetch repair schedules: %v\n", resp.StatusCode) + } + + return schedules, nil +} + func (c *Client) doJsonRequest(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) { req.Header.Set("Accept", "application/json") return c.doRequest(ctx, req, v) @@ -228,7 +263,7 @@ func (c *Client) doRequest(ctx context.Context, req *http.Request, v interface{} resp, err := c.httpClient.Do(req) if err != nil { select { - case <- ctx.Done(): + case <-ctx.Done(): return nil, ctx.Err() default: } @@ -249,19 +284,19 @@ func (c *Client) doRequest(ctx context.Context, req *http.Request, v interface{} func newCluster(state *clusterStatus) *Cluster { cluster := Cluster{ - Name: state.Name, - JmxUsername: state.JmxUsername, + Name: state.Name, + JmxUsername: state.JmxUsername, JmxPasswordSet: state.JmxPasswordSet, - Seeds: state.Seeds, - NodeState: NodeState{}, + Seeds: state.Seeds, + NodeState: NodeState{}, } for _, gs := range state.NodeStatus.EndpointStates { gossipState := GossipState{ - SourceNode: gs.SourceNode, + SourceNode: gs.SourceNode, EndpointNames: gs.EndpointNames, - TotalLoad: gs.TotalLoad, - DataCenters: map[string]DataCenterState{}, + TotalLoad: gs.TotalLoad, + DataCenters: map[string]DataCenterState{}, } for dc, dcStateInternal := range gs.Endpoints { dcState := DataCenterState{Name: dc, Racks: map[string]RackState{}} @@ -269,15 +304,15 @@ func newCluster(state *clusterStatus) *Cluster { rackState := RackState{Name: rack} for _, ep := range endpoints { endpoint := EndpointState{ - Endpoint: ep.Endpoint, - DataCenter: ep.DataCenter, - Rack: ep.Rack, - HostId: ep.HostId, - Status: ep.Status, - Severity: ep.Severity, + Endpoint: ep.Endpoint, + DataCenter: ep.DataCenter, + Rack: ep.Rack, + HostId: ep.HostId, + Status: ep.Status, + Severity: ep.Severity, ReleaseVersion: ep.ReleaseVersion, - Tokens: ep.Tokens, - Load: ep.Load, + Tokens: ep.Tokens, + Load: ep.Load, } rackState.Endpoints = append(rackState.Endpoints, endpoint) } @@ -297,4 +332,4 @@ func getBodyAsString(resp *http.Response) (string, error) { return "", err } return string(body), nil -} \ No newline at end of file +} diff --git a/reaper/types.go b/reaper/types.go index f081440..39b2655 100644 --- a/reaper/types.go +++ b/reaper/types.go @@ -1,5 +1,7 @@ package reaper +import "time" + type Cluster struct { Name string JmxUsername string @@ -46,6 +48,23 @@ type GetClusterResult struct { Error error } +type RepairSchedule struct { + Id string `json:"id"` + Owner string `json:"owner,omitempty"` + State string `json:"state,omitempty"` + Intensity float64 `json:"intensity,omitempty"` + ClusterName string `json:"cluster_name,omitempty"` + KeyspaceName string `json:"keyspace_name,omitempty"` + RepairParallism string `json:"repair_parallelism,omitempty"` + IncrementalRepair bool `json:"incremental_repair,omitempty"` + ThreadCount int `json:"repair_thread_count,omitempty"` + UnitId string `json:"repair_unit_id,omitempty"` + DaysBetween int `json:"scheduled_days_between,omitempty"` + Created time.Time `json:"creation_time,omitempty"` + Paused time.Time `json:"pause_time,omitempty"` + NextActivation time.Time `json:"next_activation,omitempty"` +} + // All the following types are used internally by the client and not part of the public API type clusterStatus struct { @@ -61,20 +80,20 @@ type nodeStatus struct { } type gossipStatus struct { - SourceNode string `json:"sourceNode"` + SourceNode string `json:"sourceNode"` EndpointNames []string `json:"endpointNames,omitempty"` - TotalLoad float64 `json:"totalLoad,omitempty"` + TotalLoad float64 `json:"totalLoad,omitempty"` Endpoints map[string]map[string][]endpointStatus } type endpointStatus struct { - Endpoint string `json:"endpoint"` - DataCenter string `json:"dc"` - Rack string `json:"rack"` - HostId string `json:"hostId"` - Status string `json:"status"` + Endpoint string `json:"endpoint"` + DataCenter string `json:"dc"` + Rack string `json:"rack"` + HostId string `json:"hostId"` + Status string `json:"status"` Severity float64 `json:"severity"` - ReleaseVersion string `json:"releaseVersion"` - Tokens string `json:"tokens"` + ReleaseVersion string `json:"releaseVersion"` + Tokens string `json:"tokens"` Load float64 `json:"load"` }