in src/ccf/virtual-ccf-provider/docker/DockerNodeProvider.cs [35:203]
public async Task<NodeEndpoint> CreateStartNode(
string nodeName,
string networkName,
List<InitialMember> initialMembers,
string? nodeLogLevel,
SecurityPolicyConfiguration policyOption,
NodeData nodeData,
List<string> san,
JsonObject? providerConfig)
{
string containerName = "cchost-nw-" + nodeName;
// Pack the start_config, members info and constitution files in a tar gz, base64 encode it
// and set that as an environment variable on the ACI instance. Then have a bootstrap
// script that unpacks the tar gz file and then launches the cchost instance.
string nodeConfigDataDir = WorkspaceDirectories.GetConfigurationDirectory(
nodeName,
networkName,
this.InfraType);
Directory.CreateDirectory(nodeConfigDataDir);
CCHostConfig cchostConfig = await CCHostConfig.InitConfig(
"templates/virtual/start-config.json",
outDir: nodeConfigDataDir);
cchostConfig.SetPublishedAddress(fqdn: containerName);
cchostConfig.SetNodeLogLevel(nodeLogLevel);
await cchostConfig.SetNodeData(nodeData);
cchostConfig.SetSubjectAltNames(san);
await cchostConfig.SetStartConfiguration(initialMembers, "constitution");
// Prepare node storage.
var nodeStorageProvider = NodeStorageProviderFactory.Create(
networkName,
providerConfig,
this.InfraType,
this.logger);
if (!await this.client.ContainerExists(containerName) &&
await nodeStorageProvider.NodeStorageDirectoryExists(nodeName))
{
// If no start node container exists but the storage folder from a previous run for
// this start node exists then delete that or else cchost startup will fail saying
// that ledger directory already exists.
this.logger.LogWarning($"Removing {nodeName} node storage folder from a previous " +
$"run before creating the start node container.");
await nodeStorageProvider.DeleteNodeStorageDirectory(nodeName);
}
await nodeStorageProvider.CreateNodeStorageDirectory(nodeName);
(var rwLedgerDir, var rwSnapshotsDir, var logsDir) =
await nodeStorageProvider.GetReadWriteLedgerSnapshotsDir(nodeName);
cchostConfig.SetLedgerSnapshotsDirectory(rwLedgerDir.MountPath, rwSnapshotsDir.MountPath);
// Write out the config file.
await cchostConfig.SaveConfig();
// Add any configuration files that the node storage provider needs during container start.
await nodeStorageProvider.AddNodeStorageProviderConfiguration(
nodeConfigDataDir,
rwLedgerDir,
rwSnapshotsDir,
roLedgerDirs: null,
roSnapshotsDir: null);
// Pack the contents of the directory into base64 encoded tar gzip string which then
// gets uncompressed and expanded in the container.
string tgzConfigData = await Utils.PackDirectory(nodeConfigDataDir);
try
{
await this.client.Networks.CreateNetworkAsync(new NetworksCreateParameters
{
Name = networkName
});
}
catch (DockerApiException de) when
(de.ResponseBody.Contains($"network with name {networkName} already exists"))
{
// Ignore already exists.
}
var createContainerParams = new CreateContainerParameters
{
Labels = new Dictionary<string, string>
{
{
DockerConstants.CcfNetworkNameTag,
networkName
},
{
DockerConstants.CcfNetworkTypeTag,
"node"
},
{
DockerConstants.CcfNetworkResourceNameTag,
nodeName
}
},
Name = containerName,
Image = $"{ImageUtils.CcfRunJsAppVirtualImage()}:{ImageUtils.CcfRunJsAppVirtualTag()}",
Env =
[
$"CONFIG_DATA_TGZ={tgzConfigData}"
],
ExposedPorts = new Dictionary<string, EmptyStruct>
{
{
$"{Ports.RpcMainPort}/tcp", new EmptyStruct()
},
{
$"{Ports.NodeToNodePort}/tcp", new EmptyStruct()
},
{
$"{Ports.RpcDebugPort}/tcp", new EmptyStruct()
}
},
HostConfig = new HostConfig
{
NetworkMode = networkName,
Devices = new List<DeviceMapping>(), // Allocate these as might be modified later.
CapAdd = new List<string>(),
Binds = new List<string>(),
PortBindings = new Dictionary<string, IList<PortBinding>>
{
{
$"{Ports.RpcMainPort}/tcp", new List<PortBinding>
{
new()
{
// Dynamic assignment.
HostPort = null
}
}
},
{
$"{Ports.RpcDebugPort}/tcp", new List<PortBinding>
{
new()
{
// Dynamic assignment.
HostPort = null
}
}
}
}
}
};
if (providerConfig.StartNodeSleep())
{
createContainerParams.Env.Add("TAIL_DEV_NULL=true");
}
if (logsDir != null)
{
createContainerParams.Env.Add($"LOGS_DIR={logsDir.MountPath}");
}
await nodeStorageProvider.UpdateCreateContainerParams(
nodeName,
roLedgerDirs: null,
roSnapshotsDir: null,
createContainerParams);
NodeEndpoint nodeEndpoint = await this.CreateAndStartNodeContainer(createContainerParams);
await this.CreateAndStartRecoveryAgentContainer(networkName, nodeEndpoint);
return nodeEndpoint;
}