/
updates.go
162 lines (134 loc) · 3.57 KB
/
updates.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
package service
import (
"context"
"errors"
"fmt"
"os"
"sync/atomic"
"github.com/google/uuid"
"github.com/vertex-center/vertex/config"
"github.com/vertex-center/vertex/core/port"
"github.com/vertex-center/vertex/core/types"
"github.com/vertex-center/vertex/pkg/event"
"github.com/vertex-center/vertex/pkg/log"
"github.com/vertex-center/vlog"
)
type UpdateService struct {
uuid uuid.UUID
ctx *types.VertexContext
adapter port.BaselinesAdapter
updaters []types.Updater // updaters containers update logic for each dependency.
updating atomic.Bool // updating is true if an update is currently in progress.
}
func NewUpdateService(ctx *types.VertexContext, adapter port.BaselinesAdapter, updaters []types.Updater) port.UpdateService {
s := &UpdateService{
uuid: uuid.New(),
ctx: ctx,
adapter: adapter,
updaters: updaters,
}
s.ctx.AddListener(s)
return s
}
func (s *UpdateService) GetUpdate(channel types.UpdatesChannel) (*types.Update, error) {
available := false
update := types.Update{}
latest, err := s.adapter.GetLatest(context.Background(), channel)
if err != nil {
return nil, err
}
log.Info("latest baseline fetched", vlog.Any("baseline", latest))
for _, updater := range s.updaters {
currentVersion, err := updater.CurrentVersion()
if err != nil {
return nil, err
}
latestVersion, err := latest.GetVersionByID(updater.ID())
if err != nil {
return nil, fmt.Errorf("'%w' when accessing '%s'", err, updater.ID())
}
if currentVersion != latestVersion {
log.Info("update available",
vlog.String("id", updater.ID()),
vlog.String("current", currentVersion),
vlog.String("latest", latestVersion))
available = true
update.Baseline = latest
}
}
if !available {
return nil, nil
}
update.Updating = s.updating.Load()
return &update, nil
}
func (s *UpdateService) InstallLatest(channel types.UpdatesChannel) error {
if !s.updating.CompareAndSwap(false, true) {
return types.ErrAlreadyUpdating
}
defer s.updating.Store(false)
latest, err := s.adapter.GetLatest(context.Background(), channel)
if err != nil {
return err
}
for _, updater := range s.updaters {
v, err := latest.GetVersionByID(updater.ID())
if err != nil {
return err
}
err = updater.Install(v)
if err != nil {
return err
}
}
s.ctx.DispatchEvent(types.EventVertexUpdated{})
return nil
}
func (s *UpdateService) firstSetup() error {
var missingDeps []types.Updater
for _, updater := range s.updaters {
if !updater.IsInstalled() {
missingDeps = append(missingDeps, updater)
}
}
if len(missingDeps) == 0 {
log.Info("all dependencies are already installed")
return nil
}
log.Info("installing missing dependencies", vlog.Any("count", len(missingDeps)))
latest, err := s.adapter.GetLatest(context.Background(), types.UpdatesChannelStable)
if err != nil {
return err
}
for _, updater := range missingDeps {
version, err := latest.GetVersionByID(updater.ID())
if err != nil {
return err
}
err = updater.Install(version)
if err != nil {
return err
}
}
return nil
}
func (s *UpdateService) OnEvent(e event.Event) error {
switch e.(type) {
case types.EventServerStart:
err := s.firstSetup()
if err != nil {
log.Error(err)
log.Error(errors.New("failed to fetch latest baseline. panic because vertex cannot run without missing dependencies"))
os.Exit(1)
}
err = config.Current.Apply()
if err != nil {
log.Error(fmt.Errorf("failed to apply the current configuration: %w", err))
os.Exit(1)
}
}
return nil
}
func (s *UpdateService) GetUUID() uuid.UUID {
return s.uuid
}