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
package atprotoidentity
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/clusterprovider"
"github.com/johnandersen777/socialweb-computer-kcp/internal/contract/clusterhome"
ctlruntime "github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime"
"github.com/johnandersen777/socialweb-computer-kcp/internal/revocation"
)
const (
Name = "atprotoidentity"
Export = "atprotoidentities"
ClusterExport = "clusters"
ConditionServiceAccountsErased = "ServiceAccountsErased"
)
type Clusters interface {
AccountClusters(ctx context.Context, account string) ([]v1alpha1.Cluster, error)
}
type Controller struct {
clusters Clusters
mu sync.Mutex
revoked map[string]string
}
func Register(m *ctlruntime.Manager, clusters Clusters) (*Controller, error) {
if clusters == nil {
return nil, errors.New("atprotoidentity: the controller needs a cluster reader")
}
if err := m.AddToScheme(v1alpha1.AddToScheme); err != nil {
return nil, err
}
c := &Controller{clusters: clusters, revoked: map[string]string{}}
if err := ctlruntime.For[*v1alpha1.AtprotoIdentity](m, Name, c, ctlruntime.Export(Export),
ctlruntime.InWorkspaces(accountOrUnresolved),
ctlruntime.WatchesIn(ClusterExport, c.clusterOf)); err != nil {
return nil, fmt.Errorf("atprotoidentity: registering the controller: %w", err)
}
return c, nil
}
func (c *Controller) Reconcile(ctx context.Context, req ctlruntime.Request, obj *v1alpha1.AtprotoIdentity) (ctlruntime.Result, error) {
if obj.Spec.Scope != v1alpha1.IdentityScopeAccount || !obj.Spec.Revoked || obj.Spec.DID == "" || !isAccount(req.Workspace) {
return ctlruntime.Result{}, nil
}
c.mu.Lock()
c.revoked[req.Workspace] = req.Cluster
c.mu.Unlock()
clusters, err := c.clusters.AccountClusters(ctx, req.Workspace)
if err != nil {
return ctlruntime.Result{}, fmt.Errorf("atprotoidentity: the clusters of %s: %w", req.Workspace, err)
}
views := make([]revocation.SettleView, 0, len(clusters))
for i := range clusters {
cl := &clusters[i]
if cl.Spec.Account != obj.Spec.DID {
continue
}
views = append(views, revocation.SettleView{
Name: cl.Name,
Settled: meta.IsStatusConditionTrue(cl.Status.Conditions, clusterprovider.ConditionRevocationSettled) || cl.DeletionTimestamp != nil,
Serving: cl.Spec.Running && cl.Status.Phase == v1alpha1.ClusterReady,
})
}
effects := revocation.Settle(views)
state := revocation.AccountState(effects)
detail := revocation.Detail(effects)
held := meta.FindStatusCondition(obj.Status.Conditions, ConditionServiceAccountsErased)
if obj.Status.State == state && held != nil && held.Message == detail {
return ctlruntime.Result{}, nil
}
settled := metav1.ConditionFalse
if state == v1alpha1.IdentityRevoked {
settled = metav1.ConditionTrue
}
obj.Status.State = state
meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
Type: ConditionServiceAccountsErased,
Status: settled,
Reason: string(state),
Message: detail,
ObservedGeneration: obj.Generation,
})
if err := req.ApplyStatus(ctx, obj); err != nil {
return ctlruntime.Result{}, err
}
return ctlruntime.Result{}, nil
}
func (c *Controller) clusterOf(_ context.Context, src ctlruntime.Request, cl *v1alpha1.Cluster) []ctlruntime.Request {
parts := strings.Split(src.Workspace, ":")
if len(parts) != 3 || cl.Spec.Account == "" {
return nil
}
account := parts[0] + ":" + parts[1]
c.mu.Lock()
logical, revoked := c.revoked[account]
c.mu.Unlock()
if !revoked {
return nil
}
return []ctlruntime.Request{{Cluster: logical, Name: clusterhome.AccountHash(cl.Spec.Account)}}
}
func isAccount(workspace string) bool {
parts := strings.Split(workspace, ":")
return len(parts) == 2 && parts[0] == clusterhome.RootWorkspace && parts[1] != ""
}
func accountOrUnresolved(workspace string) bool {
return !strings.Contains(workspace, ":") || isAccount(workspace)
}