57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
// Package planner builds offline previews. It does not inspect or change a host.
|
|
package planner
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const ProtocolVersion = 1
|
|
|
|
var (
|
|
idPattern = regexp.MustCompile(`^[a-z][a-z0-9-]{0,47}$`)
|
|
digestPattern = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
|
|
labelPattern = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`)
|
|
)
|
|
|
|
// Intent contains no secrets. ObservedStateDigest is caller-supplied in offline
|
|
// mode; a future executor must obtain it independently from the target host.
|
|
type Intent struct {
|
|
ProtocolVersion int `json:"protocolVersion"`
|
|
HostID string `json:"hostId"`
|
|
InstanceID string `json:"instanceId"`
|
|
AppID string `json:"appId"`
|
|
PackageDigest string `json:"packageDigest"`
|
|
ImageDigest string `json:"imageDigest"`
|
|
Domain string `json:"domain"`
|
|
ObservedStateDigest string `json:"observedStateDigest"`
|
|
}
|
|
|
|
func (i Intent) Validate() error {
|
|
if i.ProtocolVersion != ProtocolVersion {
|
|
return errors.New("unsupported protocol version")
|
|
}
|
|
for _, id := range []string{i.HostID, i.InstanceID, i.AppID} {
|
|
if !idPattern.MatchString(id) {
|
|
return errors.New("invalid resource identifier")
|
|
}
|
|
}
|
|
for _, digest := range []string{i.PackageDigest, i.ImageDigest, i.ObservedStateDigest} {
|
|
if !digestPattern.MatchString(digest) {
|
|
return errors.New("expected a SHA-256 digest")
|
|
}
|
|
}
|
|
labels := strings.Split(i.Domain, ".")
|
|
if len(i.Domain) > 253 || len(labels) < 2 || net.ParseIP(i.Domain) != nil {
|
|
return errors.New("expected an ASCII DNS hostname")
|
|
}
|
|
for _, label := range labels {
|
|
if !labelPattern.MatchString(label) {
|
|
return errors.New("invalid DNS hostname label")
|
|
}
|
|
}
|
|
return nil
|
|
}
|