1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package requester
import (
"errors"
"fmt"
"net/url"
"regexp"
"slices"
"strings"
commonv1 "github.com/johnandersen777/socialweb-computer-kcp/api/common/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/atproto/cloudinit"
"github.com/johnandersen777/socialweb-computer-kcp/internal/atproto/market"
)
const maxNetworkAddress = 2048
var (
networkTagRE = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$`)
dtlsFingerprintRE = regexp.MustCompile(`^sha-256 [0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){31}$`)
errShellWithoutSecrets = errors.New("a shell needs its token in spec.secretsRef: the secrets bundle writes the shell's envFile on the guest")
)
func tunnelsOptions(contract v1alpha1.ComputeContract) (cloudinit.TunnelsOptions, error) {
opts := cloudinit.TunnelsOptions{}
shell := false
for _, t := range contract.Spec.Tunnels {
tunnel := cloudinit.Tunnel{Tag: t.Tag, Port: t.Port, Server: t.Server, Transport: string(t.Transport)}
if t.Shell != nil {
tunnel.ShellEnvFile = strings.TrimSpace(t.Shell.EnvFile)
if tunnel.ShellEnvFile == "" {
tunnel.ShellEnvFile = cloudinit.DefaultShellEnvFile
}
shell = true
}
opts.Tunnels = append(opts.Tunnels, tunnel)
}
if shell && contract.Spec.SecretsRef == nil {
return opts, errShellWithoutSecrets
}
if err := opts.Validate(); err != nil {
return opts, err
}
return opts, nil
}
func ValidateNetworkAddress(kind, tag, address string) error {
if !networkTagRE.MatchString(tag) {
return fmt.Errorf("requester: tag %q is not a DNS label of at most 32 characters", tag)
}
if len(address) > maxNetworkAddress {
return fmt.Errorf("requester: the %s address is longer than %d bytes", tag, maxNetworkAddress)
}
switch kind {
case market.OnNetworkKindHTTPS:
u, err := url.Parse(address)
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil {
return fmt.Errorf("requester: the %s address %q is not an https URL", tag, address)
}
case market.OnNetworkKindWebRTC:
if !dtlsFingerprintRE.MatchString(address) {
return fmt.Errorf("requester: the %s address %q is not a DTLS fingerprint \"sha-256 XX:XX:...\"", tag, address)
}
default:
return fmt.Errorf("requester: the %s address names kind %q; a tagged address is https or webrtc", tag, kind)
}
return nil
}
func recordNetwork(state *v1alpha1.NodeClaimContract, parsed *GuestEvent) bool {
if state == nil || parsed == nil || parsed.Tag == "" {
return false
}
next := v1alpha1.ContractNetworkAddress{Tag: parsed.Tag, Kind: v1alpha1.NetworkKind(parsed.NetworkKind), Address: parsed.Address,
At: parsed.ObservedAt, Event: commonv1.StrongRef{URI: parsed.Event.URI, CID: parsed.Event.CID}}
i := slices.IndexFunc(state.Networks, func(n v1alpha1.ContractNetworkAddress) bool { return n.Tag == parsed.Tag })
if i < 0 {
state.Networks = append(state.Networks, next)
return true
}
current := state.Networks[i]
if current.Address == next.Address && current.Kind == next.Kind && current.Event.URI == next.Event.URI {
return false
}
state.Networks[i] = next
return true
}
func networkRecorded(state *v1alpha1.NodeClaimContract, parsed *GuestEvent) bool {
if state == nil || parsed == nil {
return false
}
for _, n := range state.Networks {
if n.Tag == parsed.Tag && n.Address == parsed.Address && string(n.Kind) == parsed.NetworkKind {
return true
}
}
return false
}