// Package types holds most of the types used across Keel //go:generate jsonenums -type=Notification //go:generate jsonenums -type=Level //go:generate jsonenums -type=PolicyType //go:generate jsonenums -type=TriggerType //go:generate jsonenums -type=ProviderType package types import ( "bytes" "fmt" "strconv" "strings" "time" ) // KeelDefaultPort - default port for application const KeelDefaultPort = 9300 // KeelPolicyLabel - keel update policies (version checking) const KeelPolicyLabel = "keel.sh/policy" // KeelTriggerLabel - trigger label is used to specify custom trigger types // for example keel.sh/trigger=poll would signal poll trigger to start watching for repository // changes const KeelTriggerLabel = "keel.sh/trigger" // KeelForceTagMatchLabel - label that checks whether tags match before force updating const KeelForceTagMatchLabel = "keel.sh/match-tag" // KeelPollScheduleAnnotation - optional variable to setup custom schedule for polling, defaults to @every 10m const KeelPollScheduleAnnotation = "keel.sh/pollSchedule" // KeelPollDefaultSchedule - defaul polling schedule const KeelPollDefaultSchedule = "@every 1m" // KeelDigestAnnotation - digest annotation const KeelDigestAnnotation = "keel.sh/digest" // KeelNotificationChanAnnotation - optional notification to override // default notification channel(-s) per deployment/chart const KeelNotificationChanAnnotation = "keel.sh/notify" // KeelMinimumApprovalsLabel - min approvals const KeelMinimumApprovalsLabel = "keel.sh/approvals" // KeelUpdateTimeAnnotation - update time const KeelUpdateTimeAnnotation = "keel.sh/update-time" // KeelApprovalDeadlineLabel - approval deadline const KeelApprovalDeadlineLabel = "keel.sh/approvalDeadline" // KeelApprovalDeadlineDefault - default deadline in hours const KeelApprovalDeadlineDefault = 24 // KeelPodDeleteDelay - optional delay betwen killing pods // during force deploy const KeelPodDeleteDelay = "keel.sh/forceDelay" //KeelPodMaxDelay defines maximum delay in seconds between deleting pods const KeelPodMaxDelay int64 = 600 // KeelPodTerminationGracePeriod - optional grace period during // pod termination const KeelPodTerminationGracePeriod = "keel.sh/gracePeriod" // Repository - represents main docker repository fields that // keel cares about type Repository struct { Host string `json:"host"` Name string `json:"name"` Tag string `json:"tag"` Digest string `json:"digest"` // optional digest field } // String gives you [host/]team/repo[:tag] identifier func (r *Repository) String() string { b := bytes.NewBufferString(r.Host) if b.Len() != 0 { b.WriteRune('/') } b.WriteString(r.Name) if r.Tag != "" { b.WriteRune(':') b.WriteString(r.Tag) } return b.String() } // Event - holds information about new event from trigger type Event struct { Repository Repository `json:"repository,omitempty"` CreatedAt time.Time `json:"createdAt,omitempty"` // optional field to identify trigger TriggerName string `json:"triggerName,omitempty"` } // Version - version container type Version struct { Major int64 Minor int64 Patch int64 PreRelease string Metadata string Original string } func (v Version) String() string { if v.Original != "" { return v.Original } var buf bytes.Buffer fmt.Fprintf(&buf, "%d.%d.%d", v.Major, v.Minor, v.Patch) if v.PreRelease != "" { fmt.Fprintf(&buf, "-%s", v.PreRelease) } if v.Metadata != "" { fmt.Fprintf(&buf, "+%s", v.Metadata) } return buf.String() } // TriggerType - trigger types type TriggerType int // Available trigger types const ( TriggerTypeDefault TriggerType = iota // default policy is to wait for external triggers TriggerTypePoll // poll policy sets up watchers for the affected repositories ) func (t TriggerType) String() string { switch t { case TriggerTypeDefault: return "default" case TriggerTypePoll: return "poll" default: return "default" } } // ParseTrigger - parse trigger string into type func ParseTrigger(trigger string) TriggerType { switch trigger { case "poll": return TriggerTypePoll } return TriggerTypeDefault } // PolicyType - policy type type PolicyType int // ParsePolicy - parse policy type func ParsePolicy(policy string) PolicyType { switch policy { case "all": return PolicyTypeAll case "major": return PolicyTypeMajor case "minor": return PolicyTypeMinor case "patch": return PolicyTypePatch case "force": return PolicyTypeForce default: return PolicyTypeNone } } func (t PolicyType) String() string { switch t { case PolicyTypeNone: return "none" case PolicyTypeAll: return "all" case PolicyTypeMajor: return "major" case PolicyTypeMinor: return "minor" case PolicyTypePatch: return "patch" case PolicyTypeForce: return "force" default: return "" } } // available policies const ( PolicyTypeNone PolicyType = iota PolicyTypeAll PolicyTypeMajor PolicyTypeMinor PolicyTypePatch PolicyTypeForce // update always when a new image is available ) // EventNotification notification used for sending type EventNotification struct { Name string `json:"name"` Message string `json:"message"` CreatedAt time.Time `json:"createdAt"` Type Notification `json:"type"` Level Level `json:"level"` // Channels is an optional variable to override // default channel(-s) when performing an update Channels []string `json:"-"` } // ParseEventNotificationChannels - parses deployment annotations or chart config // to get channel overrides func ParseEventNotificationChannels(annotations map[string]string) []string { channels := []string{} if annotations == nil { return channels } chanStr, ok := annotations[KeelNotificationChanAnnotation] if ok { chans := strings.Split(chanStr, ",") for _, c := range chans { channels = append(channels, strings.TrimSpace(c)) } } return channels } // ParsePodDeleteDelay - parses pod delete delay time in seconds // from a given map of annotations func ParsePodDeleteDelay(annotations map[string]string) int64 { delay := int64(0) if annotations == nil { return delay } delayStr, ok := annotations[KeelPodDeleteDelay] if !ok { return delay } g, err := strconv.Atoi(delayStr) if err != nil { return delay } if g < 1 { return delay } if int64(g) > KeelPodMaxDelay { return KeelPodMaxDelay } return int64(g) } // ParsePodTerminationGracePeriod - parses pod termination time in seconds // from a given map of annotations func ParsePodTerminationGracePeriod(annotations map[string]string) int64 { grace := int64(5) if annotations == nil { return grace } graceStr, ok := annotations[KeelPodTerminationGracePeriod] if ok { g, err := strconv.Atoi(graceStr) if err != nil { return grace } if g > 0 && g < 600 { return int64(g) } } return grace } // Notification - notification types used by notifier type Notification int // available notification types for hooks const ( PreProviderSubmitNotification Notification = iota PostProviderSubmitNotification // Kubernetes notification types NotificationPreDeploymentUpdate NotificationDeploymentUpdate // Helm notification types NotificationPreReleaseUpdate NotificationReleaseUpdate ) func (n Notification) String() string { switch n { case PreProviderSubmitNotification: return "pre provider submit" case PostProviderSubmitNotification: return "post provider submit" case NotificationPreDeploymentUpdate: return "preparing deployment update" case NotificationDeploymentUpdate: return "deployment update" case NotificationPreReleaseUpdate: return "preparing release update" case NotificationReleaseUpdate: return "release update" default: return "unknown" } } // Level - event levet type Level int // Available event levels const ( LevelDebug Level = iota LevelInfo LevelSuccess LevelWarn LevelError LevelFatal ) // ParseLevel takes a string level and returns notification level constant. func ParseLevel(lvl string) (Level, error) { switch strings.ToLower(lvl) { case "fatal": return LevelFatal, nil case "error": return LevelError, nil case "warn", "warning": return LevelWarn, nil case "info": return LevelInfo, nil case "success": return LevelSuccess, nil case "debug": return LevelDebug, nil } var l Level return l, fmt.Errorf("not a valid notification Level: %q", lvl) } func (l Level) String() string { switch l { case LevelDebug: return "debug" case LevelInfo: return "info" case LevelSuccess: return "success" case LevelWarn: return "warn" case LevelError: return "error" case LevelFatal: return "fatal" default: return "unknown" } } // Color - used to assign different colors for events func (l Level) Color() string { switch l { case LevelError: return "#F44336" case LevelInfo: return "#2196F3" case LevelSuccess: return "#00C853" case LevelFatal: return "#B71C1C" case LevelWarn: return "#FF9800" default: return "#9E9E9E" } } // ProviderType - provider type used to differentiate different providers // when used with plugins type ProviderType int // Known provider types const ( ProviderTypeUnknown ProviderType = iota ProviderTypeKubernetes ProviderTypeHelm ) func (t ProviderType) String() string { switch t { case ProviderTypeUnknown: return "unknown" case ProviderTypeKubernetes: return "kubernetes" case ProviderTypeHelm: return "helm" default: return "" } } // Approval used to store and track updates type Approval struct { // Provider name - Kubernetes/Helm Provider ProviderType `json:"provider,omitempty"` // Identifier is used to inform user about specific // Helm release or k8s deployment // ie: k8s / // helm: / Identifier string `json:"identifier,omitempty"` // Event that triggered evaluation Event *Event `json:"event,omitempty"` Message string `json:"message,omitempty"` CurrentVersion string `json:"currentVersion,omitempty"` NewVersion string `json:"newVersion,omitempty"` // Requirements for the update such as number of votes // and deadline VotesRequired int `json:"votesRequired,omitempty"` VotesReceived int `json:"votesReceived,omitempty"` // Voters is a list of voter // IDs for audit Voters []string `json:"voters,omitempty"` // Explicitly rejected approval // can be set directly by user // so even if deadline is not reached approval // could be turned down Rejected bool `json:"rejected,omitempty"` // Deadline for this request Deadline time.Time `json:"deadline,omitempty"` // When this approval was created CreatedAt time.Time `json:"createdAt,omitempty"` // WHen this approval was updated UpdatedAt time.Time `json:"updatedAt,omitempty"` } // ApprovalStatus - approval status type used in approvals // to determine whether it was rejected/approved or still pending type ApprovalStatus int // Available approval status types const ( ApprovalStatusUnknown ApprovalStatus = iota ApprovalStatusPending ApprovalStatusApproved ApprovalStatusRejected ) func (s ApprovalStatus) String() string { switch s { case ApprovalStatusPending: return "pending" case ApprovalStatusApproved: return "approved" case ApprovalStatusRejected: return "rejected" default: return "unknown" } } // Status - returns current approval status func (a *Approval) Status() ApprovalStatus { if a.Rejected { return ApprovalStatusRejected } if a.VotesReceived >= a.VotesRequired { return ApprovalStatusApproved } return ApprovalStatusPending } // Expired - checks if approval is already expired func (a *Approval) Expired() bool { return a.Deadline.Before(time.Now()) } // Delta of what's changed // ie: webhookrelay/webhook-demo:0.15.0 -> webhookrelay/webhook-demo:0.16.0 func (a *Approval) Delta() string { return fmt.Sprintf("%s -> %s", a.CurrentVersion, a.NewVersion) }