278 lines
9.5 KiB
Go
278 lines
9.5 KiB
Go
|
package linodego
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Event represents an action taken on the Account.
|
||
|
type Event struct {
|
||
|
CreatedStr string `json:"created"`
|
||
|
|
||
|
// The unique ID of this Event.
|
||
|
ID int `json:"id"`
|
||
|
|
||
|
// Current status of the Event, Enum: "failed" "finished" "notification" "scheduled" "started"
|
||
|
Status EventStatus `json:"status"`
|
||
|
|
||
|
// The action that caused this Event. New actions may be added in the future.
|
||
|
Action EventAction `json:"action"`
|
||
|
|
||
|
// A percentage estimating the amount of time remaining for an Event. Returns null for notification events.
|
||
|
PercentComplete int `json:"percent_complete"`
|
||
|
|
||
|
// The rate of completion of the Event. Only some Events will return rate; for example, migration and resize Events.
|
||
|
Rate *string `json:"rate"`
|
||
|
|
||
|
// If this Event has been read.
|
||
|
Read bool `json:"read"`
|
||
|
|
||
|
// If this Event has been seen.
|
||
|
Seen bool `json:"seen"`
|
||
|
|
||
|
// The estimated time remaining until the completion of this Event. This value is only returned for in-progress events.
|
||
|
TimeRemainingMsg json.RawMessage `json:"time_remaining"`
|
||
|
TimeRemaining *int `json:"-"`
|
||
|
|
||
|
// The username of the User who caused the Event.
|
||
|
Username string `json:"username"`
|
||
|
|
||
|
// Detailed information about the Event's entity, including ID, type, label, and URL used to access it.
|
||
|
Entity *EventEntity `json:"entity"`
|
||
|
|
||
|
// When this Event was created.
|
||
|
Created *time.Time `json:"-"`
|
||
|
}
|
||
|
|
||
|
// EventAction constants start with Action and include all known Linode API Event Actions.
|
||
|
type EventAction string
|
||
|
|
||
|
// EventAction constants represent the actions that cause an Event. New actions may be added in the future.
|
||
|
const (
|
||
|
ActionBackupsEnable EventAction = "backups_enable"
|
||
|
ActionBackupsCancel EventAction = "backups_cancel"
|
||
|
ActionBackupsRestore EventAction = "backups_restore"
|
||
|
ActionCommunityQuestionReply EventAction = "community_question_reply"
|
||
|
ActionCreateCardUpdated EventAction = "credit_card_updated"
|
||
|
ActionDiskCreate EventAction = "disk_create"
|
||
|
ActionDiskDelete EventAction = "disk_delete"
|
||
|
ActionDiskDuplicate EventAction = "disk_duplicate"
|
||
|
ActionDiskImagize EventAction = "disk_imagize"
|
||
|
ActionDiskResize EventAction = "disk_resize"
|
||
|
ActionDNSRecordCreate EventAction = "dns_record_create"
|
||
|
ActionDNSRecordDelete EventAction = "dns_record_delete"
|
||
|
ActionDNSZoneCreate EventAction = "dns_zone_create"
|
||
|
ActionDNSZoneDelete EventAction = "dns_zone_delete"
|
||
|
ActionImageDelete EventAction = "image_delete"
|
||
|
ActionLinodeAddIP EventAction = "linode_addip"
|
||
|
ActionLinodeBoot EventAction = "linode_boot"
|
||
|
ActionLinodeClone EventAction = "linode_clone"
|
||
|
ActionLinodeCreate EventAction = "linode_create"
|
||
|
ActionLinodeDelete EventAction = "linode_delete"
|
||
|
ActionLinodeDeleteIP EventAction = "linode_deleteip"
|
||
|
ActionLinodeMigrate EventAction = "linode_migrate"
|
||
|
ActionLinodeMutate EventAction = "linode_mutate"
|
||
|
ActionLinodeReboot EventAction = "linode_reboot"
|
||
|
ActionLinodeRebuild EventAction = "linode_rebuild"
|
||
|
ActionLinodeResize EventAction = "linode_resize"
|
||
|
ActionLinodeShutdown EventAction = "linode_shutdown"
|
||
|
ActionLinodeSnapshot EventAction = "linode_snapshot"
|
||
|
ActionLongviewClientCreate EventAction = "longviewclient_create"
|
||
|
ActionLongviewClientDelete EventAction = "longviewclient_delete"
|
||
|
ActionManagedDisabled EventAction = "managed_disabled"
|
||
|
ActionManagedEnabled EventAction = "managed_enabled"
|
||
|
ActionManagedServiceCreate EventAction = "managed_service_create"
|
||
|
ActionManagedServiceDelete EventAction = "managed_service_delete"
|
||
|
ActionNodebalancerCreate EventAction = "nodebalancer_create"
|
||
|
ActionNodebalancerDelete EventAction = "nodebalancer_delete"
|
||
|
ActionNodebalancerConfigCreate EventAction = "nodebalancer_config_create"
|
||
|
ActionNodebalancerConfigDelete EventAction = "nodebalancer_config_delete"
|
||
|
ActionPasswordReset EventAction = "password_reset"
|
||
|
ActionPaymentSubmitted EventAction = "payment_submitted"
|
||
|
ActionStackScriptCreate EventAction = "stackscript_create"
|
||
|
ActionStackScriptDelete EventAction = "stackscript_delete"
|
||
|
ActionStackScriptPublicize EventAction = "stackscript_publicize"
|
||
|
ActionStackScriptRevise EventAction = "stackscript_revise"
|
||
|
ActionTFADisabled EventAction = "tfa_disabled"
|
||
|
ActionTFAEnabled EventAction = "tfa_enabled"
|
||
|
ActionTicketAttachmentUpload EventAction = "ticket_attachment_upload"
|
||
|
ActionTicketCreate EventAction = "ticket_create"
|
||
|
ActionTicketReply EventAction = "ticket_reply"
|
||
|
ActionVolumeAttach EventAction = "volume_attach"
|
||
|
ActionVolumeClone EventAction = "volume_clone"
|
||
|
ActionVolumeCreate EventAction = "volume_create"
|
||
|
ActionVolumeDelte EventAction = "volume_delete"
|
||
|
ActionVolumeDetach EventAction = "volume_detach"
|
||
|
ActionVolumeResize EventAction = "volume_resize"
|
||
|
)
|
||
|
|
||
|
// EntityType constants start with Entity and include Linode API Event Entity Types
|
||
|
type EntityType string
|
||
|
|
||
|
// EntityType contants are the entities an Event can be related to
|
||
|
const (
|
||
|
EntityLinode EntityType = "linode"
|
||
|
EntityDisk EntityType = "disk"
|
||
|
)
|
||
|
|
||
|
// EventStatus constants start with Event and include Linode API Event Status values
|
||
|
type EventStatus string
|
||
|
|
||
|
// EventStatus constants reflect the current status of an Event
|
||
|
const (
|
||
|
EventFailed EventStatus = "failed"
|
||
|
EventFinished EventStatus = "finished"
|
||
|
EventNotification EventStatus = "notification"
|
||
|
EventScheduled EventStatus = "scheduled"
|
||
|
EventStarted EventStatus = "started"
|
||
|
)
|
||
|
|
||
|
// EventEntity provides detailed information about the Event's
|
||
|
// associated entity, including ID, Type, Label, and a URL that
|
||
|
// can be used to access it.
|
||
|
type EventEntity struct {
|
||
|
// ID may be a string or int, it depends on the EntityType
|
||
|
ID interface{} `json:"id"`
|
||
|
Label string `json:"label"`
|
||
|
Type EntityType `json:"type"`
|
||
|
URL string `json:"url"`
|
||
|
}
|
||
|
|
||
|
// EventsPagedResponse represents a paginated Events API response
|
||
|
type EventsPagedResponse struct {
|
||
|
*PageOptions
|
||
|
Data []Event `json:"data"`
|
||
|
}
|
||
|
|
||
|
// endpoint gets the endpoint URL for Event
|
||
|
func (EventsPagedResponse) endpoint(c *Client) string {
|
||
|
endpoint, err := c.Events.Endpoint()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return endpoint
|
||
|
}
|
||
|
|
||
|
// endpointWithID gets the endpoint URL for a specific Event
|
||
|
func (e Event) endpointWithID(c *Client) string {
|
||
|
endpoint, err := c.Events.Endpoint()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
endpoint = fmt.Sprintf("%s/%d", endpoint, e.ID)
|
||
|
return endpoint
|
||
|
}
|
||
|
|
||
|
// appendData appends Events when processing paginated Event responses
|
||
|
func (resp *EventsPagedResponse) appendData(r *EventsPagedResponse) {
|
||
|
resp.Data = append(resp.Data, r.Data...)
|
||
|
}
|
||
|
|
||
|
// ListEvents gets a collection of Event objects representing actions taken
|
||
|
// on the Account. The Events returned depend on the token grants and the grants
|
||
|
// of the associated user.
|
||
|
func (c *Client) ListEvents(ctx context.Context, opts *ListOptions) ([]Event, error) {
|
||
|
response := EventsPagedResponse{}
|
||
|
err := c.listHelper(ctx, &response, opts)
|
||
|
for i := range response.Data {
|
||
|
response.Data[i].fixDates()
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return response.Data, nil
|
||
|
}
|
||
|
|
||
|
// GetEvent gets the Event with the Event ID
|
||
|
func (c *Client) GetEvent(ctx context.Context, id int) (*Event, error) {
|
||
|
e, err := c.Events.Endpoint()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
e = fmt.Sprintf("%s/%d", e, id)
|
||
|
r, err := c.R(ctx).SetResult(&Event{}).Get(e)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return r.Result().(*Event).fixDates(), nil
|
||
|
}
|
||
|
|
||
|
// fixDates converts JSON timestamps to Go time.Time values
|
||
|
func (e *Event) fixDates() *Event {
|
||
|
e.Created, _ = parseDates(e.CreatedStr)
|
||
|
e.TimeRemaining = unmarshalTimeRemaining(e.TimeRemainingMsg)
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
// MarkEventRead marks a single Event as read.
|
||
|
func (c *Client) MarkEventRead(ctx context.Context, event *Event) error {
|
||
|
e := event.endpointWithID(c)
|
||
|
e = fmt.Sprintf("%s/read", e)
|
||
|
|
||
|
_, err := coupleAPIErrors(c.R(ctx).Post(e))
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// MarkEventsSeen marks all Events up to and including this Event by ID as seen.
|
||
|
func (c *Client) MarkEventsSeen(ctx context.Context, event *Event) error {
|
||
|
e := event.endpointWithID(c)
|
||
|
e = fmt.Sprintf("%s/seen", e)
|
||
|
|
||
|
_, err := coupleAPIErrors(c.R(ctx).Post(e))
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func unmarshalTimeRemaining(m json.RawMessage) *int {
|
||
|
jsonBytes, err := m.MarshalJSON()
|
||
|
if err != nil {
|
||
|
panic(jsonBytes)
|
||
|
}
|
||
|
|
||
|
if len(jsonBytes) == 4 && string(jsonBytes) == "null" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var timeStr string
|
||
|
if err := json.Unmarshal(jsonBytes, &timeStr); err == nil && len(timeStr) > 0 {
|
||
|
if dur, err := durationToSeconds(timeStr); err != nil {
|
||
|
panic(err)
|
||
|
} else {
|
||
|
return &dur
|
||
|
}
|
||
|
} else {
|
||
|
var intPtr int
|
||
|
if err := json.Unmarshal(jsonBytes, &intPtr); err == nil {
|
||
|
return &intPtr
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log.Println("[WARN] Unexpected unmarshalTimeRemaining value: ", jsonBytes)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// durationToSeconds takes a hh:mm:ss string and returns the number of seconds
|
||
|
func durationToSeconds(s string) (int, error) {
|
||
|
multipliers := [3]int{60 * 60, 60, 1}
|
||
|
segs := strings.Split(s, ":")
|
||
|
if len(segs) > len(multipliers) {
|
||
|
return 0, fmt.Errorf("too many ':' separators in time duration: %s", s)
|
||
|
}
|
||
|
var d int
|
||
|
l := len(segs)
|
||
|
for i := 0; i < l; i++ {
|
||
|
m, err := strconv.Atoi(segs[i])
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
d += m * multipliers[i+len(multipliers)-l]
|
||
|
}
|
||
|
return d, nil
|
||
|
}
|