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
package marketnetwork
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/app/compose"
"github.com/johnandersen777/socialweb-computer-kcp/internal/devinstance"
)
const (
GatewayChildSpecFile = "gateway-spec.json"
GatewayChildSessionsFile = "owner-sessions.json"
GatewayChildCluster = "tenant"
OwnerAccountDomain = "alice.test"
)
func readOwnerDid(path string) (string, error) {
sessions, err := compose.ReadOwnerSessions(path)
if err != nil {
return "", err
}
dids := make([]string, 0, len(sessions))
for did := range sessions {
dids = append(dids, did)
}
if len(dids) == 0 {
return "", fmt.Errorf("marketnetwork: the owner sessions file %s names no account", path)
}
sort.Strings(dids)
return dids[0], nil
}
func ownerAccount(id, handle string) compose.PdsAccount {
return compose.PdsAccount{Vault: NetworkVaultServer, Mount: NetworkOwnerMount, Path: "identity/owner-" + id + "/pds-session",
Handle: handle, Pds: NetworkAlicePdsName}
}
func ensureOwner(ctx context.Context, c *compose.Composer, r compose.Root, id, path, pdsURL, handle string) (string, error) {
if did, session, err := compose.FirstOwnerSession(path); err == nil {
if session.Service == pdsURL {
return did, nil
}
session.Service = pdsURL
b, err := json.Marshal(map[string]compose.OwnerSession{did: session})
if err != nil {
return "", err
}
return did, compose.WriteFileAtomic(path, b, 0o600)
} else if !os.IsNotExist(err) {
return "", err
}
account := ownerAccount(id, handle)
links := compose.PdsAccountLinks(r, account)
if err := c.ApplyLinks(ctx, r, "owner-"+id, links); err != nil {
return "", err
}
if ok, msg := c.LinksAccepted(ctx, r, links); !ok {
return "", errors.New(msg)
}
did, err := c.EnsurePdsAccount(ctx, r, account)
if err != nil {
return "", err
}
kv, err := c.KV(ctx, r, account.Vault, account.Mount+"/identity/*")
if err != nil {
return "", err
}
data, err := compose.ReadKV(ctx, kv, account.Mount, account.Path)
if err != nil {
return "", err
}
password, _ := data["password"].(string)
if password == "" {
return "", fmt.Errorf("marketnetwork: the owner account %s has no password in the network vault", handle)
}
b, err := json.Marshal(map[string]compose.OwnerSession{did: {Service: pdsURL, Identifier: handle, Password: password}})
if err != nil {
return "", err
}
return did, compose.WriteFileAtomic(path, b, 0o600)
}
func GatewayChildOf(ctx context.Context, c *compose.Composer, r compose.Root, id, stateDir string, app v1alpha1.NetworkApp,
value v1alpha1.MarketNetworkValue, aliceURL string, bidderDids []string, suspend bool) (*devinstance.GatewayChild, error) {
if err := os.MkdirAll(stateDir, 0o700); err != nil {
return nil, err
}
sessions := app.OwnerSessionsFile
var did string
var err error
if sessions == "" {
sessions = filepath.Join(stateDir, GatewayChildSessionsFile)
did, err = ensureOwner(ctx, c, r, id, sessions, aliceURL, id+"-owner."+OwnerAccountDomain)
} else {
did, err = readOwnerDid(sessions)
}
if err != nil {
return nil, fmt.Errorf("the owner account of %s's tenant cluster: %w", id, err)
}
spec := v1alpha1.GatewaySpec{Profile: app.Profile, Suspend: suspend, Network: &value, Options: app.Options, BidderDids: append([]string(nil), bidderDids...),
MarketNetwork: &v1alpha1.GatewayMarketNetwork{OwnerSessionsFile: sessions}, ClusterProvider: app.ClusterProvider, Quota: app.Quota,
Components: app.Components}
b, err := json.MarshalIndent(spec, "", " ")
if err != nil {
return nil, err
}
path := filepath.Join(stateDir, GatewayChildSpecFile)
if have, err := os.ReadFile(path); err != nil || !bytes.Equal(have, b) {
if err := compose.WriteFileAtomic(path, b, 0o600); err != nil {
return nil, err
}
}
return &devinstance.GatewayChild{SpecFile: path, BootstrapCluster: GatewayChildCluster, BootstrapAccountDID: did}, nil
}