240 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
package spec
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"k8s.io/client-go/pkg/api/meta"
 | 
						|
	"k8s.io/client-go/pkg/api/unversioned"
 | 
						|
	"k8s.io/client-go/pkg/api/v1"
 | 
						|
)
 | 
						|
 | 
						|
var alphaRegexp = regexp.MustCompile("^[a-zA-Z]*$")
 | 
						|
 | 
						|
type MaintenanceWindow struct {
 | 
						|
	StartTime    time.Time    // Start time
 | 
						|
	StartWeekday time.Weekday // Start weekday
 | 
						|
 | 
						|
	EndTime    time.Time    // End time
 | 
						|
	EndWeekday time.Weekday // End weekday
 | 
						|
}
 | 
						|
 | 
						|
type Volume struct {
 | 
						|
	Size         string `json:"size"`
 | 
						|
	StorageClass string `json:"storageClass"`
 | 
						|
}
 | 
						|
 | 
						|
type PostgresqlParam struct {
 | 
						|
	PgVersion  string            `json:"version"`
 | 
						|
	Parameters map[string]string `json:"parameters"`
 | 
						|
}
 | 
						|
 | 
						|
type ResourceDescription struct {
 | 
						|
	Cpu    string `json:"cpu"`
 | 
						|
	Memory string `json:"memory"`
 | 
						|
}
 | 
						|
 | 
						|
type Resources struct {
 | 
						|
	ResourceRequest ResourceDescription `json:"requests,omitempty""`
 | 
						|
	ResourceLimits  ResourceDescription `json:"limits,omitempty""`
 | 
						|
}
 | 
						|
 | 
						|
type Patroni struct {
 | 
						|
	InitDB               map[string]string `json:"initdb"`
 | 
						|
	PgHba                []string          `json:"pg_hba"`
 | 
						|
	TTL                  uint32            `json:"ttl"`
 | 
						|
	LoopWait             uint32            `json:"loop_wait"`
 | 
						|
	RetryTimeout         uint32            `json:"retry_timeout"`
 | 
						|
	MaximumLagOnFailover float32           `json:"maximum_lag_on_failover"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213
 | 
						|
}
 | 
						|
 | 
						|
type UserFlags []string
 | 
						|
 | 
						|
type PostgresStatus string
 | 
						|
 | 
						|
const (
 | 
						|
	ClusterStatusUnknown      PostgresStatus = ""
 | 
						|
	ClusterStatusCreating     PostgresStatus = "Creating"
 | 
						|
	ClusterStatusUpdating     PostgresStatus = "Updating"
 | 
						|
	ClusterStatusUpdateFailed PostgresStatus = "UpdateFailed"
 | 
						|
	ClusterStatusAddFailed    PostgresStatus = "CreateFailed"
 | 
						|
	ClusterStatusRunning      PostgresStatus = "Running"
 | 
						|
)
 | 
						|
 | 
						|
// PostgreSQL Third Party (resource) Object
 | 
						|
type Postgresql struct {
 | 
						|
	unversioned.TypeMeta `json:",inline"`
 | 
						|
	Metadata             v1.ObjectMeta `json:"metadata"`
 | 
						|
 | 
						|
	Spec   PostgresSpec   `json:"spec"`
 | 
						|
	Status PostgresStatus `json:"status"`
 | 
						|
}
 | 
						|
 | 
						|
type PostgresSpec struct {
 | 
						|
	PostgresqlParam `json:"postgresql"`
 | 
						|
	Volume          `json:"volume,omitempty"`
 | 
						|
	Patroni         `json:"patroni,omitempty"`
 | 
						|
	Resources       `json:"resources,omitempty"`
 | 
						|
 | 
						|
	TeamId              string               `json:"teamId"`
 | 
						|
	AllowedSourceRanges []string             `json:"allowedSourceRanges"`
 | 
						|
	NumberOfInstances   int32                `json:"numberOfInstances"`
 | 
						|
	Users               map[string]UserFlags `json:"users"`
 | 
						|
	MaintenanceWindows  []MaintenanceWindow  `json:"maintenanceWindows,omitempty"`
 | 
						|
	ClusterName         string               `json:"-"`
 | 
						|
}
 | 
						|
 | 
						|
type PostgresqlList struct {
 | 
						|
	unversioned.TypeMeta `json:",inline"`
 | 
						|
	Metadata             unversioned.ListMeta `json:"metadata"`
 | 
						|
 | 
						|
	Items []Postgresql `json:"items"`
 | 
						|
}
 | 
						|
 | 
						|
func parseTime(s string) (t time.Time, wd time.Weekday, wdProvided bool, err error) {
 | 
						|
	var timeLayout string
 | 
						|
 | 
						|
	parts := strings.Split(s, ":")
 | 
						|
	if len(parts) == 3 {
 | 
						|
		if len(parts[0]) != 3 || !alphaRegexp.MatchString(parts[0]) {
 | 
						|
			err = fmt.Errorf("Weekday must be 3 characters length")
 | 
						|
			return
 | 
						|
		}
 | 
						|
		timeLayout = "Mon:15:04"
 | 
						|
		wdProvided = true
 | 
						|
	} else {
 | 
						|
		wdProvided = false
 | 
						|
		timeLayout = "15:04"
 | 
						|
	}
 | 
						|
 | 
						|
	tp, err := time.Parse(timeLayout, s)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	wd = tp.Weekday()
 | 
						|
	t = tp.UTC()
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (m *MaintenanceWindow) MarshalJSON() ([]byte, error) {
 | 
						|
	var startWd, endWd string
 | 
						|
	if m.StartWeekday == time.Sunday && m.EndWeekday == time.Saturday {
 | 
						|
		startWd = ""
 | 
						|
		endWd = ""
 | 
						|
	} else {
 | 
						|
		startWd = m.StartWeekday.String()[:3] + ":"
 | 
						|
		endWd = m.EndWeekday.String()[:3] + ":"
 | 
						|
	}
 | 
						|
 | 
						|
	return []byte(fmt.Sprintf("\"%s%s-%s%s\"",
 | 
						|
		startWd, m.StartTime.Format("15:04"),
 | 
						|
		endWd, m.EndTime.Format("15:04"))), nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error {
 | 
						|
	var (
 | 
						|
		got                 MaintenanceWindow
 | 
						|
		weekdayProvidedFrom bool
 | 
						|
		weekdayProvidedTo   bool
 | 
						|
		err                 error
 | 
						|
	)
 | 
						|
 | 
						|
	parts := strings.Split(string(data[1:len(data)-1]), "-")
 | 
						|
	if len(parts) != 2 {
 | 
						|
		return fmt.Errorf("Incorrect maintenance window format")
 | 
						|
	}
 | 
						|
 | 
						|
	got.StartTime, got.StartWeekday, weekdayProvidedFrom, err = parseTime(parts[0])
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	got.EndTime, got.EndWeekday, weekdayProvidedTo, err = parseTime(parts[1])
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if got.EndTime.Before(got.StartTime) {
 | 
						|
		return fmt.Errorf("'From' time must be prior to the 'To' time.")
 | 
						|
	}
 | 
						|
 | 
						|
	if !weekdayProvidedFrom || !weekdayProvidedTo {
 | 
						|
		got.StartWeekday = time.Sunday
 | 
						|
		got.EndWeekday = time.Saturday
 | 
						|
	}
 | 
						|
 | 
						|
	*m = got
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *Postgresql) GetObjectKind() unversioned.ObjectKind {
 | 
						|
	return &p.TypeMeta
 | 
						|
}
 | 
						|
 | 
						|
func (p *Postgresql) GetObjectMeta() meta.Object {
 | 
						|
	return &p.Metadata
 | 
						|
}
 | 
						|
 | 
						|
func (pl *PostgresqlList) GetObjectKind() unversioned.ObjectKind {
 | 
						|
	return &pl.TypeMeta
 | 
						|
}
 | 
						|
 | 
						|
func (pl *PostgresqlList) GetListMeta() unversioned.List {
 | 
						|
	return &pl.Metadata
 | 
						|
}
 | 
						|
 | 
						|
// The code below is used only to work around a known problem with third-party
 | 
						|
// resources and ugorji. If/when these issues are resolved, the code below
 | 
						|
// should no longer be required.
 | 
						|
//
 | 
						|
type PostgresqlListCopy PostgresqlList
 | 
						|
type PostgresqlCopy Postgresql
 | 
						|
 | 
						|
func clusterName(clusterName string, teamName string) (string, error) {
 | 
						|
	teamNameLen := len(teamName)
 | 
						|
	if len(clusterName) < teamNameLen+2 {
 | 
						|
		return "", fmt.Errorf("Name is too short")
 | 
						|
	}
 | 
						|
	if strings.ToLower(clusterName[:teamNameLen+1]) != strings.ToLower(teamName)+"-" {
 | 
						|
		return "", fmt.Errorf("Name must start with the team name and dash")
 | 
						|
	}
 | 
						|
 | 
						|
	return clusterName[teamNameLen+1:], nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *Postgresql) UnmarshalJSON(data []byte) error {
 | 
						|
	tmp := PostgresqlCopy{}
 | 
						|
	err := json.Unmarshal(data, &tmp)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	tmp2 := Postgresql(tmp)
 | 
						|
 | 
						|
	clusterName, err := clusterName(tmp2.Metadata.Name, tmp2.Spec.TeamId)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	tmp2.Spec.ClusterName = clusterName
 | 
						|
	*p = tmp2
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (pl *PostgresqlList) UnmarshalJSON(data []byte) error {
 | 
						|
	tmp := PostgresqlListCopy{}
 | 
						|
	err := json.Unmarshal(data, &tmp)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	tmp2 := PostgresqlList(tmp)
 | 
						|
	*pl = tmp2
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |