-
Notifications
You must be signed in to change notification settings - Fork 4
/
gateway_fqdn_deployer.go
232 lines (187 loc) · 7.29 KB
/
gateway_fqdn_deployer.go
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package deployer
import (
"context"
"github.com/pkg/errors"
client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node"
"github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads"
"github.com/threefoldtech/zos/pkg/gridtypes"
)
// GatewayFQDNDeployer for deploying a GatewayFqdn
type GatewayFQDNDeployer struct {
tfPluginClient *TFPluginClient
deployer MockDeployer
}
// NewGatewayFqdnDeployer generates new gateway fqdn deployer
func NewGatewayFqdnDeployer(tfPluginClient *TFPluginClient) GatewayFQDNDeployer {
deployer := NewDeployer(*tfPluginClient, true)
gatewayFQDN := GatewayFQDNDeployer{
tfPluginClient: tfPluginClient,
deployer: &deployer,
}
return gatewayFQDN
}
// Validate validates gateway FQDN deployer
func (d *GatewayFQDNDeployer) Validate(ctx context.Context, gw *workloads.GatewayFQDNProxy) error {
sub := d.tfPluginClient.SubstrateConn
if len(gw.Name) == 0 {
return errors.New("gateway workload must have a name")
}
if err := validateAccountBalanceForExtrinsics(sub, d.tfPluginClient.Identity); err != nil {
return err
}
nodeClient, err := d.tfPluginClient.NcPool.GetNodeClient(sub, gw.NodeID)
if err != nil {
return errors.Wrapf(err, "failed to get node client with ID %d", gw.NodeID)
}
cfg, err := nodeClient.NetworkGetPublicConfig(ctx)
if err != nil {
return errors.Wrapf(err, "couldn't get node %d public config", gw.NodeID)
}
if cfg.IPv4.IP == nil {
return errors.Errorf("node %d doesn't contain a public IP in its public config", gw.NodeID)
}
return client.AreNodesUp(ctx, sub, []uint32{gw.NodeID}, d.tfPluginClient.NcPool)
}
// GenerateVersionlessDeployments generates deployments for gatewayFqdn deployer without versions
func (d *GatewayFQDNDeployer) GenerateVersionlessDeployments(ctx context.Context, gw *workloads.GatewayFQDNProxy) (map[uint32]gridtypes.Deployment, error) {
deployments := make(map[uint32]gridtypes.Deployment)
var err error
dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{})
dl.Workloads = append(dl.Workloads, gw.ZosWorkload())
dl.Metadata, err = gw.GenerateMetadata()
if err != nil {
return nil, errors.Wrapf(err, "failed to generate gateway FQDN deployment %s metadata", gw.Name)
}
deployments[gw.NodeID] = dl
return deployments, nil
}
// Deploy deploys the GatewayFQDN deployments using the deployer
func (d *GatewayFQDNDeployer) Deploy(ctx context.Context, gw *workloads.GatewayFQDNProxy) error {
if err := d.Validate(ctx, gw); err != nil {
return err
}
newDeployments, err := d.GenerateVersionlessDeployments(ctx, gw)
if err != nil {
return errors.Wrap(err, "could not generate deployments data")
}
// TODO: solution providers
newDeploymentsSolutionProvider := make(map[uint32]*uint64)
newDeploymentsSolutionProvider[gw.NodeID] = nil
gw.NodeDeploymentID, err = d.deployer.Deploy(ctx, gw.NodeDeploymentID, newDeployments, newDeploymentsSolutionProvider)
// update state
// error is not returned immediately before updating state because of untracked failed deployments
if contractID, ok := gw.NodeDeploymentID[gw.NodeID]; ok && contractID != 0 {
gw.ContractID = contractID
if !workloads.Contains(d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID], gw.ContractID) {
d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID] = append(d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID], gw.ContractID)
}
}
return err
}
// BatchDeploy deploys multiple deployments using the deployer
func (d *GatewayFQDNDeployer) BatchDeploy(ctx context.Context, gws []*workloads.GatewayFQDNProxy) error {
newDeployments := make(map[uint32][]gridtypes.Deployment)
newDeploymentsSolutionProvider := make(map[uint32][]*uint64)
for _, gw := range gws {
if err := d.Validate(ctx, gw); err != nil {
return err
}
dls, err := d.GenerateVersionlessDeployments(ctx, gw)
if err != nil {
return errors.Wrap(err, "could not generate deployments data")
}
for nodeID, dl := range dls {
// solution providers
newDeploymentsSolutionProvider[nodeID] = nil
if _, ok := newDeployments[nodeID]; !ok {
newDeployments[nodeID] = []gridtypes.Deployment{dl}
continue
}
newDeployments[nodeID] = append(newDeployments[nodeID], dl)
}
}
newDls, err := d.deployer.BatchDeploy(ctx, newDeployments, newDeploymentsSolutionProvider)
// update state
// error is not returned immediately before updating state because of untracked failed deployments
for _, gw := range gws {
if err := d.updateStateFromDeployments(ctx, gw, newDls); err != nil {
return errors.Wrapf(err, "failed to update gateway fqdn '%s' state", gw.Name)
}
}
return err
}
// Cancel cancels a gateway deployment
func (d *GatewayFQDNDeployer) Cancel(ctx context.Context, gw *workloads.GatewayFQDNProxy) (err error) {
if err := d.Validate(ctx, gw); err != nil {
return err
}
contractID := gw.NodeDeploymentID[gw.NodeID]
err = d.deployer.Cancel(ctx, contractID)
if err != nil {
return err
}
// update state
gw.ContractID = 0
delete(gw.NodeDeploymentID, gw.NodeID)
d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID] = workloads.Delete(d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID], contractID)
return nil
}
func (d *GatewayFQDNDeployer) updateStateFromDeployments(ctx context.Context, gw *workloads.GatewayFQDNProxy, newDls map[uint32][]gridtypes.Deployment) error {
gw.NodeDeploymentID = map[uint32]uint64{}
for _, newDl := range newDls[gw.NodeID] {
dlData, err := workloads.ParseDeploymentData(newDl.Metadata)
if err != nil {
return errors.Wrapf(err, "could not get deployment %d data", newDl.ContractID)
}
if dlData.Name == gw.Name {
gw.NodeDeploymentID[gw.NodeID] = newDl.ContractID
}
}
if contractID, ok := gw.NodeDeploymentID[gw.NodeID]; ok && contractID != 0 {
gw.ContractID = contractID
if !workloads.Contains(d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID], gw.ContractID) {
d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID] = append(d.tfPluginClient.State.CurrentNodeDeployments[gw.NodeID], gw.ContractID)
}
}
return nil
}
// TODO: check sync added or not ??
func (d *GatewayFQDNDeployer) syncContracts(ctx context.Context, gw *workloads.GatewayFQDNProxy) (err error) {
if err := d.tfPluginClient.SubstrateConn.DeleteInvalidContracts(gw.NodeDeploymentID); err != nil {
return err
}
if len(gw.NodeDeploymentID) == 0 {
gw.ContractID = 0
}
return nil
}
// Sync syncs the gateway deployments
func (d *GatewayFQDNDeployer) Sync(ctx context.Context, gw *workloads.GatewayFQDNProxy) error {
if err := d.syncContracts(ctx, gw); err != nil {
return errors.Wrap(err, "could not sync contracts")
}
dls, err := d.deployer.GetDeployments(ctx, gw.NodeDeploymentID)
if err != nil {
return errors.Wrap(err, "could not get deployment objects")
}
dl := dls[gw.NodeID]
wl, _ := dl.Get(gridtypes.Name(gw.Name))
gwWorkload := workloads.GatewayFQDNProxy{}
gw.Backends = gwWorkload.Backends
gw.Name = gwWorkload.Name
gw.FQDN = gwWorkload.FQDN
gw.TLSPassthrough = gwWorkload.TLSPassthrough
gw.Network = gwWorkload.Network
if wl != nil && wl.Result.State.IsOkay() {
gwWorkload, err := workloads.NewGatewayFQDNProxyFromZosWorkload(*wl.Workload)
gw.Backends = gwWorkload.Backends
gw.Name = gwWorkload.Name
gw.FQDN = gwWorkload.FQDN
gw.TLSPassthrough = gwWorkload.TLSPassthrough
gw.Network = gwWorkload.Network
if err != nil {
return err
}
}
return nil
}