public void Update()
 {
     _processRunner.RunShell("apt-get update", new RunnerOptions
     {
         Env = new Dictionary <string, string>
         {
             { "APT_CONFIG", _aptDirectoryPrepService.AptConfigFile },
             { "DEBIAN_FRONTEND", "noninteractive" },
             { "DEBCONF_NONINTERACTIVE_SEEN", "true" },
             { "LC_ALL", "C" },
             { "LANGUAGE", "C" },
             { "LANG", "C" }
         }
     });
 }
Exemple #2
0
        public void GenerateRootFs(string directory, bool overwrite, bool runStage2)
        {
            if (!Env.IsRoot)
            {
                _logger.LogWarning("The currently running user is not root. Some commands will be run with sudo.");
            }

            _logger.LogInformation("Updating the package cache...");
            _aptGetService.Update();

            if (runStage2)
            {
                // Let's make sure arch-chroot is available.
                if (!File.Exists("/usr/bin/arch-chroot"))
                {
                    _logger.LogError($"You indicated you wanted to run stage2, but arch-chroot isn't available. Try running {"sudo apt-get install arch-install-scripts".Quoted()}.");
                    throw new Exception("The command arch-chroot isn't available.");
                }
            }

            var image     = GetImage();
            var imageLock = GetImageLock();

            var preseedFiles = GetPreseeds(image);
            var scripts      = GetScripts(image);

            if (string.IsNullOrEmpty(directory))
            {
                directory = "rootfs";
            }

            if (!Path.IsPathRooted(directory))
            {
                directory = Path.Combine(Directory.GetCurrentDirectory(), directory);
            }

            directory = Path.GetFullPath(directory);

            _logger.LogInformation("Generating rootfs at {directory}.", directory);

            _logger.LogInformation("Cleaning directory...");
            if (overwrite)
            {
                if (Directory.Exists(directory))
                {
                    _logger.LogInformation("Directory exists, removing...");
                    _processRunner.RunShell($"rm -rf {directory.Quoted()}", new RunnerOptions {
                        UseSudo = !Env.IsRoot
                    });
                }
                _processRunner.RunShell($"mkdir {directory.Quoted()}", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });
            }
            else
            {
                if (!Directory.Exists(directory))
                {
                    _logger.LogInformation("Creating directory..");
                    _processRunner.RunShell($"mkdir {directory.Quoted()}", new RunnerOptions {
                        UseSudo = !Env.IsRoot
                    });
                }
                else
                {
                    // Make sure it is empty.
                    if (Directory.GetFiles(directory).Length > 0 || Directory.GetDirectories(directory, "*").Length > 0)
                    {
                        throw new Exception("The directory is not empty.");
                    }
                }
            }

            _logger.LogInformation("Creating required folders/files...");
            _processRunner.RunShell("mkdir -p \"var/lib/dpkg/info\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("touch \"var/lib/dpkg/available\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("touch \"var/lib/dpkg/status\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("mkdir -p \"var/lib/dpkg/updates\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("mkdir -p \"etc/apt\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("mkdir -p \"etc/apt\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("mkdir -p \"stage2/debs\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });
            _processRunner.RunShell("mkdir -p \"stage2/preseeds\"", new RunnerOptions {
                UseSudo = !Env.IsRoot, WorkingDirectory = directory
            });

            _logger.LogInformation("Including apt repositories...");
            foreach (var repo in GetRepositories(image))
            {
                _processRunner.RunShell($"echo {repo.ToString().Quoted()} | tee -a ./etc/apt/sources.list",
                                        new RunnerOptions {
                    UseSudo = !Env.IsRoot, WorkingDirectory = directory
                });
            }

            // Download the packages.
            _logger.LogInformation("Downloading the debs...");
            var debFolder = Path.Combine(_workspaceConfig.RootDirectory, ".debs");

            debFolder.EnsureDirectoryExists();
            _aptGetService.Download(imageLock.InstalledPackages.ToDictionary(x => x.Key, x => x.Value.Version), debFolder);

            // Debs will go in here to reinstall in stage 2.
            var stage2DebDirectory = Path.Combine(directory, "stage2", "debs");

            var stage2Script = new StringBuilder();

            stage2Script.AppendLine("#!/bin/sh");
            stage2Script.AppendLine("set -e");
            stage2Script.AppendLine("export DEBIAN_FRONTEND=noninteractive");
            stage2Script.AppendLine("export DEBCONF_NONINTERACTIVE_SEEN=true ");
            stage2Script.AppendLine("export LC_ALL=C");
            stage2Script.AppendLine("export LANGUAGE=C");
            stage2Script.AppendLine("export LANG=C");
            stage2Script.AppendLine($"export APT_TOOL_ROOTFS_NATIVE_DIR={directory.Quoted()}");

            // Step 1, run all the preseeds.
            foreach (var preseedFile in preseedFiles)
            {
                var fileName = Path.GetFileName(preseedFile) + Guid.NewGuid().ToString().Replace("-", "");
                _processRunner.RunShell($"cp {preseedFile.Quoted()} {$"stage2/preseeds/{fileName}".Quoted()}", new RunnerOptions {
                    UseSudo = !Env.IsRoot, WorkingDirectory = directory
                });
                stage2Script.AppendLine($"debconf-set-selections {$"/stage2/preseeds/{fileName}".Quoted()}");
            }

            foreach (var package in imageLock.InstalledPackages.Keys)
            {
                var installedPackage = imageLock.InstalledPackages[package];
                var debFile          = Path.Combine(debFolder, $"{package}_{installedPackage.Version.Version.Replace(":", "%3a")}_{installedPackage.Version.Architecture}.deb");
                if (!File.Exists(debFile))
                {
                    throw new Exception($"The deb file {debFile} doesn't exist.");
                }

                _logger.LogInformation("Extracting: {package}", imageLock.InstalledPackages[package].Version.ToCommandParameter(package));

                // Step 2, we extract the entire contents of the of the deb package.
                // This won't actually configure the package as installed, but it
                // will at least allow out chroot to execute commands (dpkg, etc).
                _dpkgService.Extract(debFile, directory, !Env.IsRoot);

                // Step 3, move the deb file into the rootfs for a stage2 setup.
                // In here, we will simply unpack the dpkg file, but not configure
                // it (only execute preinst).
                _processRunner.RunShell($"cp \"{debFile}\" \"{stage2DebDirectory}\"", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });
                stage2Script.AppendLine($"dpkg --unpack --force-confnew  --force-overwrite --force-depends /stage2/debs/{Path.GetFileName(debFile)}");
            }

            // Step 4, within the stage2, now we configure all the packages that we have unpacked.
            stage2Script.AppendLine("dpkg --configure -a");

            if (scripts.Count >= 0)
            {
                _logger.LogInformation("Including the custom scripts in the stage2...");
                var destinationDirectory = Path.Combine(directory, "stage2", "scripts");
                _processRunner.RunShell($"mkdir -p {destinationDirectory.Quoted()}", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });

                foreach (var scriptLookup in scripts.ToLookup(x => x.Directory))
                {
                    var destinationScriptDirectoryName = $"{Path.GetFileName(scriptLookup.Key)}-{Guid.NewGuid().ToString().Replace("-", "")}";
                    var destinationScriptDirectory     = Path.Combine(destinationDirectory, destinationScriptDirectoryName);
                    _processRunner.RunShell($"mkdir {destinationScriptDirectory}", new RunnerOptions {
                        UseSudo = !Env.IsRoot
                    });
                    _processRunner.RunShell($"cp -ra {scriptLookup.Key}/* {destinationScriptDirectory}/", new RunnerOptions {
                        UseSudo = !Env.IsRoot
                    });

                    foreach (var script in scriptLookup)
                    {
                        stage2Script.AppendLine(
                            $"bash -c \"cd /stage2/scripts/{destinationScriptDirectoryName} && ./{script.Name}\"");
                    }
                }
            }

            if (!runStage2) // Done put this message if we will be deleting this ourselves.
            {
                stage2Script.AppendLine("echo \"DONE! Don't forget to delete /stage2!!\"");
            }

            _logger.LogInformation("Saving stage2 script to be run via chroot: {script}", "/stage2/stage2.sh");

            var stage2ScriptPath = Path.Combine(directory, "stage2", "stage2.sh");
            var tempPath         = Path.GetTempFileName();

            try
            {
                File.WriteAllText(tempPath, stage2Script.ToString());
                _processRunner.RunShell($"cp \"{tempPath}\" \"{stage2ScriptPath}\"", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });
            }
            finally
            {
                File.Delete(tempPath);
            }

            _processRunner.RunShell($"chmod +x {stage2ScriptPath}", new RunnerOptions {
                UseSudo = !Env.IsRoot
            });

            if (runStage2)
            {
                _logger.LogInformation("Running stage2...");
                _processRunner.RunShell($"arch-chroot {directory.Quoted()} /stage2/stage2.sh", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });
                _processRunner.RunShell($"rm -r {Path.Combine(directory, "stage2").Quoted()}", new RunnerOptions {
                    UseSudo = !Env.IsRoot
                });
            }

            _logger.LogInformation("Done!");
        }