-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBootstrap.java
More file actions
212 lines (192 loc) · 7.22 KB
/
Bootstrap.java
File metadata and controls
212 lines (192 loc) · 7.22 KB
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
package bootstrap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import metrics.collectors.MetricServer;
import model.lightchain.Identifier;
import modules.logger.LightchainLogger;
import modules.logger.Logger;
/**
* Bootstrap is a class that creates a bootstrap file for the nodes. The bootstrap file is a text file
* that contains the nodes to be bootstrapped. Each line of the file contains a node identifier and
* the node's address.
*/
public class Bootstrap {
/**
* Port number at which the node will be listening for incoming connections from other nodes.
*/
public static final int bootstrapPortNumber = 8082;
private static final Random random = new Random();
private final String bootstrapFileName = "bootstrap.txt"; // Don't change this name, it is used in the Dockerfile.
private final String bootstrapKeyName = "node";
private final Logger logger = LightchainLogger.getLogger(Bootstrap.class.getCanonicalName());
private final short nodeCount;
/**
* The docker names is a list that contains the names of the docker containers.
* This list is used to create containers with the same name on the LocalTestNet.
* This is necessary for containers to have the same name as their address in this bootstrap id table.
* Otherwise, the containers may not be able to find each other on the LocalTestNet docker network.
*/
private final List<String> dockerNames = new ArrayList<>();
private final List<Identifier> identifiers = new ArrayList<>();
/**
* The id table is a map that contains the node's identifier and the node's address.
*/
private final HashMap<Identifier, String> idTable;
/**
* The metrics table is a map that contains the node's identifier and the node's metrics address.
*/
private final HashMap<Identifier, String> metricsTable;
/**
* Constructor for Bootstrap.
*
* @param nodeCount number of nodes to be created.
*/
public Bootstrap(short nodeCount) {
this.nodeCount = nodeCount;
this.idTable = new HashMap<>();
this.metricsTable = new HashMap<>();
}
/**
* Reads the bootstrap file and returns a map containing the node's identifier and the node's address.
*
* @param path path to the bootstrap file.
* @return a map containing the node's identifier and the node's address.
* @throws IOException if the bootstrap file cannot be read. The exception is irrecoverable and the caller should exit.
*/
public static Map<Identifier, String> readFile(String path) throws IOException {
Map<Identifier, String> map = new HashMap<>();
File idTableFile = new File(path);
InputStream inputStream = new FileInputStream(idTableFile);
Reader readerStream = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(readerStream);
String line;
while ((line = reader.readLine()) != null) {
String[] split = line.split(":");
map.put(new Identifier(split[0]), split[1] + ":" + split[2]);
}
reader.close();
return map;
}
/**
* Builds the bootstrap file. The bootstrap file is a text file that contains the nodes to be
* bootstrapped. Each line of the file contains a node identifier and the node's address.
* The bootstrap file is written to the output file.
*/
public BootstrapInfo build() {
this.makeBootstrap();
this.writeOnFile();
this.print();
return new BootstrapInfo(this.identifiers, this.dockerNames, this.bootstrapFileName, this.idTable, this.metricsTable);
}
/**
* Builds and returns a ConcurrentMap of nodes to be bootstrapped.
*/
private void makeBootstrap() {
for (int i = 0; i < this.nodeCount; i++) {
Identifier id = this.newIdentifier();
while (idTable.containsKey(id)) {
this.logger.warn("id {} already exists, generating a new one", id);
id = this.newIdentifier();
}
String dockerName = bootstrapKeyName.toLowerCase() + i;
this.dockerNames.add(dockerName);
this.identifiers.add(id);
this.idTable.put(id, dockerName + ":" + bootstrapPortNumber);
// TODO: server port should be a parameter.
this.metricsTable.put(id, dockerName + ":" + MetricServer.SERVER_PORT);
}
}
/**
* Writes the bootstrap file to the output file.
*/
void writeOnFile() {
File file = new File(bootstrapFileName);
try {
FileOutputStream fileStream = new FileOutputStream(file);
Writer writer = new OutputStreamWriter(fileStream, StandardCharsets.UTF_8);
for (Map.Entry<Identifier, String> id : this.idTable.entrySet()) {
writer.write(id.getKey().toString() + ":" + this.idTable.get(id.getKey()) + "\n");
}
writer.flush();
writer.close();
} catch (IOException e) {
this.logger.fatal("could not write bootstrap file", e);
}
}
/**
* Prints the id table on the console.
*/
public void print() {
logger.info("bootstrap file created with the following content:");
for (Map.Entry<Identifier, String> id : this.idTable.entrySet()) {
logger.info(id.getKey() + " " + this.idTable.get(id.getKey()));
}
logger.info("bootstrap file written to " + bootstrapFileName);
}
/**
* Generates a random identifier.
* Note: this is a temporary method to generate random identifiers. In mature LightChain, the Identifier of a node
* is the hash of the public key of the node.
*
* @return a random identifier.
*/
private model.lightchain.Identifier newIdentifier() {
byte[] bytes = new byte[model.lightchain.Identifier.Size];
random.nextBytes(bytes);
return new model.lightchain.Identifier(bytes);
}
/**
* Returns the docker names of the nodes in the form of a list.
*
* @return the docker names of the nodes in the form of a list.
*/
public List<String> getDockerNames() {
return new ArrayList<>(dockerNames);
}
/**
* Returns the identifiers of the nodes in the form of a list.
*
* @return the identifiers of the nodes in the form of a list.
*/
public List<Identifier> getIdentifiers() {
return new ArrayList<>(identifiers);
}
/**
* Returns the id table, which is a map that contains the node's identifier and the node's address.
*
* @return the id table, which is a map that contains the node's identifier and the node's address.
*/
public HashMap<Identifier, String> getIdTable() {
return new HashMap<>(idTable);
}
/**
* Returns the metrics table, which is a map that contains the node's identifier and the node's metrics address.
*
* @return the metrics table, which is a map that contains the node's identifier and the node's metrics address.
*/
public HashMap<Identifier, String> getMetricsTable() {
return new HashMap<>(metricsTable);
}
/**
* Returns the name of the bootstrap file.
*
* @return the name of the bootstrap file.
*/
public String getBootstrapFileName() {
return bootstrapFileName;
}
}