-
Notifications
You must be signed in to change notification settings - Fork 660
/
manager.go
152 lines (121 loc) · 3.63 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
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package vms
import (
"context"
"errors"
"fmt"
"sync"
"golang.org/x/exp/maps"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/utils/logging"
)
var (
ErrNotFound = errors.New("not found")
_ Manager = (*manager)(nil)
)
// A Factory creates new instances of a VM
type Factory interface {
New(logging.Logger) (interface{}, error)
}
// Manager tracks a collection of VM factories, their aliases, and their
// versions.
// 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
// 4. Manage the versions of VMs
type Manager interface {
ids.Aliaser
// Return a factory that can create new instances of the vm whose ID is
// [vmID]
GetFactory(vmID ids.ID) (Factory, error)
// Map [vmID] to [factory]. [factory] creates new instances of the vm whose
// ID is [vmID]
RegisterFactory(ctx context.Context, vmID ids.ID, factory Factory) error
// ListFactories returns all the IDs that have had factories registered.
ListFactories() ([]ids.ID, error)
// Versions returns the primary alias of the VM mapped to the reported
// version of the VM for all the registered VMs that reported versions.
Versions() (map[string]string, error)
}
type manager struct {
// Note: The string representation of a VM's ID is also considered to be an
// alias of the VM. That is, [vmID].String() is an alias for [vmID].
ids.Aliaser
log logging.Logger
lock sync.RWMutex
// 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
}
// NewManager returns an instance of a VM manager
func NewManager(log logging.Logger, aliaser ids.Aliaser) Manager {
return &manager{
Aliaser: aliaser,
log: log,
factories: make(map[ids.ID]Factory),
versions: make(map[ids.ID]string),
}
}
func (m *manager) GetFactory(vmID ids.ID) (Factory, error) {
m.lock.RLock()
defer m.lock.RUnlock()
if factory, ok := m.factories[vmID]; ok {
return factory, nil
}
return nil, fmt.Errorf("%q was %w", vmID, ErrNotFound)
}
func (m *manager) RegisterFactory(ctx context.Context, vmID ids.ID, factory Factory) error {
m.lock.Lock()
defer m.lock.Unlock()
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
vm, err := factory.New(m.log)
if err != nil {
return err
}
commonVM, ok := vm.(common.VM)
if !ok {
return nil
}
version, err := commonVM.Version(ctx)
if err != nil {
// Drop the shutdown error to surface the original error
_ = commonVM.Shutdown(ctx)
return err
}
m.versions[vmID] = version
return commonVM.Shutdown(ctx)
}
func (m *manager) ListFactories() ([]ids.ID, error) {
m.lock.RLock()
defer m.lock.RUnlock()
return maps.Keys(m.factories), nil
}
func (m *manager) Versions() (map[string]string, error) {
m.lock.RLock()
defer m.lock.RUnlock()
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
}