public async Task CreateStartNode()

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;
    }