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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package exposure
import (
"context"
"errors"
"fmt"
"strings"
"time"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
exposurev1alpha1 "github.com/johnandersen777/socialweb-computer-kcp/api/exposure/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/atproto/ingress"
"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kube"
"github.com/johnandersen777/socialweb-computer-kcp/internal/kit/conds"
)
const (
ReasonIngressPending = "Pending"
ReasonIngressRegistered = "Registered"
)
var subscriberReasons = map[exposurev1alpha1.IngressSubscriberState]string{
exposurev1alpha1.IngressSubscriberConnecting: "Connecting",
exposurev1alpha1.IngressSubscriberRegistered: ReasonIngressRegistered,
exposurev1alpha1.IngressSubscriberReconnecting: "Reconnecting",
exposurev1alpha1.IngressSubscriberWaiting: "Waiting",
exposurev1alpha1.IngressSubscriberClosed: "Closed",
}
func SubscriberStateOf(status ingress.SubscriberStatus) exposurev1alpha1.ExposureIngressSubscriber {
out := exposurev1alpha1.ExposureIngressSubscriber{
State: exposurev1alpha1.IngressSubscriberState(status.State),
LastError: status.LastError,
CloseCode: int32(status.CloseCode),
RelayVersion: status.RelayVersion,
}
if !status.RegisteredAt.IsZero() {
at := metav1.NewTime(status.RegisteredAt.UTC().Truncate(time.Second))
out.RegisteredAt = &at
}
return out
}
func IngressRegistered(sub *exposurev1alpha1.ExposureIngressSubscriber) (bool, string, string) {
if sub == nil || sub.State == "" {
return false, ReasonIngressPending, "no ingress subscriber has reported its state"
}
reason, ok := subscriberReasons[sub.State]
if !ok {
return false, ReasonIngressPending, fmt.Sprintf("the ingress subscriber reports an unknown state %q", sub.State)
}
if sub.State == exposurev1alpha1.IngressSubscriberRegistered {
message := "the ingress subscriber is registered at the relay"
if sub.RegisteredAt != nil {
message += " since " + sub.RegisteredAt.UTC().Format(time.RFC3339)
}
if sub.RelayVersion != "" {
message += " (relay " + sub.RelayVersion + ")"
}
return true, reason, message
}
message := "the ingress subscriber is " + string(sub.State)
if sub.CloseCode != 0 {
message += fmt.Sprintf(" after close code %d", sub.CloseCode)
}
if sub.LastError != "" {
message += ": " + sub.LastError
}
return false, reason, message
}
func SetIngressRegistered(conditions *[]metav1.Condition, generation int64, route *exposurev1alpha1.ExposureIngress,
sub *exposurev1alpha1.ExposureIngressSubscriber) {
if route == nil && sub == nil {
meta.RemoveStatusCondition(conditions, exposurev1alpha1.ConditionIngressRegistered)
return
}
ok, reason, message := IngressRegistered(sub)
conds.Set(conditions, generation, exposurev1alpha1.ConditionIngressRegistered, ok, reason, message)
}
func IngressRegisteredCurrent(obj *exposurev1alpha1.Exposure) bool {
var route *exposurev1alpha1.ExposureIngress
var sub *exposurev1alpha1.ExposureIngressSubscriber
if obj.Spec.Mode == exposurev1alpha1.ExposureModeIngress && obj.Status.Ingress != nil {
route, sub = obj.Status.Ingress, obj.Status.Ingress.Subscriber
if route.Host == "" && route.URL == "" {
route = nil
}
}
want := append([]metav1.Condition(nil), obj.Status.Conditions...)
SetIngressRegistered(&want, obj.Generation, route, sub)
have := meta.FindStatusCondition(obj.Status.Conditions, exposurev1alpha1.ConditionIngressRegistered)
next := meta.FindStatusCondition(want, exposurev1alpha1.ConditionIngressRegistered)
switch {
case have == nil || next == nil:
return have == nil && next == nil
default:
return have.Status == next.Status && have.Reason == next.Reason && have.Message == next.Message
}
}
func SubscriberStateEqual(a, b *exposurev1alpha1.ExposureIngressSubscriber) bool {
return equality.Semantic.DeepEqual(a, b)
}
const SubscriberStatusPath = "ingress.subscriber"
func SubscriberStatusFields(state exposurev1alpha1.ExposureIngressSubscriber) map[string]any {
subscriber := map[string]any{"state": string(state.State), "registeredAt": nil, "lastError": nil, "closeCode": nil, "relayVersion": nil}
if state.RegisteredAt != nil {
subscriber["registeredAt"] = state.RegisteredAt.UTC().Format(time.RFC3339)
}
if state.LastError != "" {
subscriber["lastError"] = state.LastError
}
if state.CloseCode != 0 {
subscriber["closeCode"] = int64(state.CloseCode)
}
if state.RelayVersion != "" {
subscriber["relayVersion"] = state.RelayVersion
}
return map[string]any{"ingress": map[string]any{"subscriber": subscriber}}
}
func ReportSubscriberState(ctx context.Context, kcp *rest.Config, workspace, exposureName string,
state exposurev1alpha1.ExposureIngressSubscriber) error {
workspace, exposureName = strings.TrimSpace(workspace), strings.TrimSpace(exposureName)
if workspace == "" || exposureName == "" {
return errors.New("exposure: reporting an ingress subscriber state needs a workspace and an Exposure name")
}
if _, ok := subscriberReasons[state.State]; !ok {
return fmt.Errorf("exposure: the ingress subscriber state %q is not one of connecting, registered, reconnecting, waiting, closed", state.State)
}
cl, err := kube.ForWorkspace(kcp, workspace)
if err != nil {
return err
}
doc := kube.Doc{"apiVersion": exposurev1alpha1.GroupVersion.String(), "kind": "Exposure", "metadata": map[string]any{"name": exposureName},
"status": SubscriberStatusFields(state)}
_, err = kube.Apply[exposurev1alpha1.Exposure](ctx, cl.Resource(exposurev1alpha1.ExposureGVR), doc,
kube.ApplyOptions{FieldManager: exposurev1alpha1.FieldManagerIngressSubscriber, Force: true, Status: true})
if err != nil {
return fmt.Errorf("exposure: reporting the ingress subscriber of %s in %s: %w", exposureName, workspace, err)
}
return nil
}