Files: b507f49239a73767abbee979b4e637a6ca469764 / index.js
4288 bytesRaw
1 | const Graph = require('ipld-graph-builder') |
2 | const Message = require('primea-message') |
3 | const Kernel = require('./kernel.js') |
4 | const Scheduler = require('./scheduler.js') |
5 | const DFSchecker = require('./dfsChecker.js') |
6 | |
7 | const ROOT_ID = 'zdpuAm6aTdLVMUuiZypxkwtA7sKm7BWERy8MPbaCrFsmiyzxr' |
8 | |
9 | module.exports = class Hypervisor { |
10 | /** |
11 | * The Hypervisor manages the container instances by instantiating them and |
12 | * destorying them when possible. It also facilitates localating Containers |
13 | * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api) |
14 | * @param {object} state - the starting state |
15 | */ |
16 | constructor (dag, state = {}) { |
17 | this.graph = new Graph(dag) |
18 | this.scheduler = new Scheduler() |
19 | this.state = state |
20 | this._containerTypes = {} |
21 | this._nodesToCheck = new Set() |
22 | } |
23 | |
24 | /** |
25 | * add a potaintail node in the state graph to check for garbage collection |
26 | * @param {string} id |
27 | */ |
28 | addNodeToCheck (id) { |
29 | this._nodesToCheck.add(id) |
30 | } |
31 | |
32 | /** |
33 | * given a port, this finds the corridsponeding endpoint port of the channel |
34 | * @param {object} port |
35 | * @returns {Promise} |
36 | */ |
37 | getDestPort (port) { |
38 | if (port.destPort) { |
39 | return port.destPort |
40 | } else { |
41 | return this.graph.get(this.state, `${port.destId}/ports/${port.destName}`) |
42 | } |
43 | } |
44 | |
45 | // loads an instance of a container from the state |
46 | async _loadInstance (id) { |
47 | const state = await this.graph.get(this.state, id) |
48 | const container = this._containerTypes[state.type] |
49 | |
50 | // create a new kernel instance |
51 | const kernel = new Kernel({ |
52 | hypervisor: this, |
53 | state: state, |
54 | container: container, |
55 | id: id |
56 | }) |
57 | |
58 | // save the newly created instance |
59 | this.scheduler.update(kernel) |
60 | return kernel |
61 | } |
62 | |
63 | /** |
64 | * gets an existsing container instances |
65 | * @param {string} id - the containers ID |
66 | * @returns {Promise} |
67 | */ |
68 | async getInstance (id) { |
69 | let instance = this.scheduler.getInstance(id) |
70 | if (instance) { |
71 | return instance |
72 | } else { |
73 | const resolve = this.scheduler.getLock(id) |
74 | const instance = await this._loadInstance(id) |
75 | resolve(instance) |
76 | return instance |
77 | } |
78 | } |
79 | |
80 | /** |
81 | * creates an new container instances and save it in the state |
82 | * @param {string} type - the type of container to create |
83 | * @param {*} code |
84 | * @param {array} entryPorts |
85 | * @param {object} id |
86 | * @param {object} id.nonce |
87 | * @param {object} id.parent |
88 | * @returns {Promise} |
89 | */ |
90 | async createInstance (type, code, entryPorts = [], id = {nonce: 0, parent: null}) { |
91 | // create a lock to prevent the scheduler from reloving waits before the |
92 | // new container is loaded |
93 | const resolve = this.scheduler.getLock(id) |
94 | const idHash = await this.getHashFromObj(id) |
95 | const state = { |
96 | nonce: [0], |
97 | ports: {}, |
98 | type: type, |
99 | code: code |
100 | } |
101 | |
102 | // save the container in the state |
103 | await this.graph.set(this.state, idHash, state) |
104 | // create the container instance |
105 | const exoInterface = await this._loadInstance(idHash) |
106 | |
107 | resolve(exoInterface) |
108 | // send the intialization message |
109 | exoInterface.queue(null, new Message({ |
110 | ports: entryPorts |
111 | })) |
112 | |
113 | return exoInterface |
114 | } |
115 | |
116 | /** |
117 | * creates a state root starting from a given container and a given number of |
118 | * ticks |
119 | * @param {Number} ticks the number of ticks at which to create the state root |
120 | * @returns {Promise} |
121 | */ |
122 | async createStateRoot (ticks) { |
123 | await this.scheduler.wait(ticks) |
124 | const unlinked = await DFSchecker(this.graph, this.state, ROOT_ID, this._nodesToCheck) |
125 | unlinked.forEach(id => { |
126 | delete this.state[id] |
127 | }) |
128 | return this.graph.flush(this.state) |
129 | } |
130 | |
131 | /** |
132 | * regirsters a container with the hypervisor |
133 | * @param {String} type - the name of the type |
134 | * @param {Class} Constructor - a Class for instantiating the container |
135 | * @param {*} args - any args that the contructor takes |
136 | */ |
137 | registerContainer (type, Constructor, args) { |
138 | this._containerTypes[type] = { |
139 | Constructor: Constructor, |
140 | args: args |
141 | } |
142 | } |
143 | |
144 | /** |
145 | * get a hash from a POJO |
146 | * @param {object} obj |
147 | * @return {Promise} |
148 | */ |
149 | async getHashFromObj (obj) { |
150 | return (await this.graph.flush(obj))['/'] |
151 | } |
152 | } |
153 |
Built with git-ssb-web