keel/types/types.go

509 lines
12 KiB
Go

// 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 <namespace>/<deployment name>
// helm: <namespace>/<release name>
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)
}