-
Notifications
You must be signed in to change notification settings - Fork 668
/
manager.go
167 lines (140 loc) · 4.74 KB
/
manager.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
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package vms
import (
"fmt"
"sync"
"github.com/ava-labs/avalanchego/api/server"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/logging"
)
// A Factory creates new instances of a VM
type Factory interface {
New(*snow.Context) (interface{}, error)
}
// Manager is a VM manager.
// It has the following functionality:
// 1) Register a VM factory. To register a VM is to associate its ID with a
// VMFactory which, when New() is called upon it, creates a new instance of that VM.
// 2) Get a VM factory. Given the ID of a VM that has been
// registered, return the factory that the ID is associated with.
// 3) Manage the aliases of VMs
type Manager interface {
ids.Aliaser
// Returns a factory that can create new instances of the VM
// with the given ID
GetFactory(ids.ID) (Factory, error)
// Associate an ID with the factory that creates new instances
// of the VM with the given ID
RegisterFactory(ids.ID, Factory) error
// Versions returns the versions of all the VMs that have been registered
Versions() (map[string]string, error)
}
// Implements Manager
type manager struct {
// Note: The string representation of a VM's ID is also considered to be an
// alias of the VM. That is, [VM].String() is an alias for the VM, too.
ids.Aliaser
// Key: A VM's ID
// Value: A factory that creates new instances of that VM
factories map[ids.ID]Factory
// Key: A VM's ID
// Value: version the VM returned
versions map[ids.ID]string
// The node's API server.
// [manager] adds routes to this server to expose new API endpoints/services
apiServer *server.Server
log logging.Logger
}
// NewManager returns an instance of a VM manager
func NewManager(apiServer *server.Server, log logging.Logger) Manager {
return &manager{
Aliaser: ids.NewAliaser(),
factories: make(map[ids.ID]Factory),
versions: make(map[ids.ID]string),
apiServer: apiServer,
log: log,
}
}
// Return a factory that can create new instances of the vm whose
// ID is [vmID]
func (m *manager) GetFactory(vmID ids.ID) (Factory, error) {
if factory, ok := m.factories[vmID]; ok {
return factory, nil
}
return nil, fmt.Errorf("%q was not registered as a vm", vmID)
}
// Map [vmID] to [factory]. [factory] creates new instances of the vm whose
// ID is [vmID]
func (m *manager) RegisterFactory(vmID ids.ID, factory Factory) error {
if _, exists := m.factories[vmID]; exists {
return fmt.Errorf("%q was already registered as a vm", vmID)
}
if err := m.Alias(vmID, vmID.String()); err != nil {
return err
}
m.factories[vmID] = factory
// VMs can expose a static API (one that does not depend on the state of a
// particular chain.) This adds to the node's API server the static API of
// the VM with ID [vmID]. This allows clients to call the VM's static API
// methods.
m.log.Debug("adding static API for vm %q", vmID)
vm, err := factory.New(nil)
if err != nil {
return err
}
commonVM, ok := vm.(common.VM)
if !ok {
return nil
}
version, err := commonVM.Version()
if err != nil {
m.log.Error("fetching version for %q errored with: %s", vmID, err)
if err := commonVM.Shutdown(); err != nil {
return fmt.Errorf("shutting down VM errored with: %s", err)
}
return nil
}
m.versions[vmID] = version
handlers, err := commonVM.CreateStaticHandlers()
if err != nil {
m.log.Error("creating static API endpoints for %q errored with: %s", vmID, err)
if err := commonVM.Shutdown(); err != nil {
return fmt.Errorf("shutting down VM errored with: %s", err)
}
return nil
}
// all static endpoints go to the vm endpoint, defaulting to the vm id
defaultEndpoint := constants.VMAliasPrefix + vmID.String()
// use a single lock for this entire vm
lock := new(sync.RWMutex)
// register the static endpoints
for extension, service := range handlers {
m.log.Verbo("adding static API endpoint: %s%s", defaultEndpoint, extension)
if err := m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log); err != nil {
return fmt.Errorf(
"failed to add static API endpoint %s%s: %s",
defaultEndpoint,
extension,
err,
)
}
}
return nil
}
// Versions returns the primary alias of the VM mapped to the reported version
// of the VM for all the registered VMs that reported versions.
func (m *manager) Versions() (map[string]string, error) {
versions := make(map[string]string, len(m.versions))
for vmID, version := range m.versions {
alias, err := m.PrimaryAlias(vmID)
if err != nil {
return nil, err
}
versions[alias] = version
}
return versions, nil
}