Пример #1
0
        private static async Task ExecuteAsync(OutputContext output, DirectoryInfo directory)
        {
            var opulenceFilePath = DirectorySearch.AscendingSearch(directory.FullName, "opulence.json");

            if (opulenceFilePath != null)
            {
                output.WriteInfoLine($"found 'opulence.json' at '{Path.GetDirectoryName(opulenceFilePath)}'");
                return;
            }

            output.WriteInfoLine("locating nearest sln file");
            var solutionFilePath = DirectorySearch.AscendingWildcardSearch(directory.FullName, "*.sln").FirstOrDefault()?.FullName;

            if (opulenceFilePath == null && solutionFilePath != null && Confirm(output, $"use '{Path.GetDirectoryName(solutionFilePath)}' as root?"))
            {
                opulenceFilePath = Path.Combine(Path.GetDirectoryName(solutionFilePath) !, "opulence.json");
            }

            if (opulenceFilePath == null && Confirm(output, "use project directory as root?"))
            {
                opulenceFilePath = Path.Combine(directory.FullName, "opulence.json");
            }

            if (opulenceFilePath == null)
            {
                throw new CommandException("cannot determine root directory");
            }

            var config = new OpulenceConfig()
            {
                Container = new ContainerConfig()
                {
                    Registry = new RegistryConfig(),
                }
            };

            while (true)
            {
                output.WriteAlways("entry the container registry hostname (ex: example.azurecr.io): ");
                var line = Console.ReadLine();
                output.WriteAlwaysLine(string.Empty);

                if (!string.IsNullOrEmpty(line))
                {
                    config.Container.Registry.Hostname = line.Trim();
                    break;
                }
            }

            using var stream = File.OpenWrite(opulenceFilePath);
            await JsonSerializer.SerializeAsync(stream, config, new JsonSerializerOptions()
            {
                WriteIndented = true,
            });

            output.WriteInfo($"initialized opulence config at '{opulenceFilePath}'");
        }
Пример #2
0
        private static async Task PackageApplicationAsync(OutputContext output, Application application, string applicationName, string environment)
        {
            using var step = output.BeginStep("Deploying Application Manifests...");

            using var tempFile = TempFile.Create();
            output.WriteInfoLine($"Writing output to '{tempFile.FilePath}'.");

            {
                using var stream = File.OpenWrite(tempFile.FilePath);
                using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);

                if (application.Globals.DeploymentKind == DeploymentKind.None)
                {
                    // No extra steps
                }
                else if (application.Globals.DeploymentKind == DeploymentKind.Kubernetes)
                {
                    await ApplicationYamlWriter.WriteAsync(output, writer, application);
                }
                else if (application.Globals.DeploymentKind == DeploymentKind.Oam)
                {
                    await OamApplicationGenerator.WriteOamApplicationAsync(writer, output, application, applicationName, environment);
                }
                else
                {
                    throw new InvalidOperationException($"Unknown DeploymentKind: " + application.Globals.DeploymentKind);
                }
            }

            output.WriteDebugLine("Running 'kubectl apply'.");
            output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"kubectl",
                $"apply -f \"{tempFile.FilePath}\"",
                System.Environment.CurrentDirectory,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'kubectl apply' failed.");
            }

            output.WriteInfoLine($"Deployed application '{applicationName}'.");

            step.MarkComplete();
        }
Пример #3
0
        public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            if (container.UseMultiphaseDockerfile == false)
            {
                throw new CommandException("Generated Dockerfile workflow does not support single-phase Dockerfiles.");
            }

            container.UseMultiphaseDockerfile ??= true;

            var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), "Dockerfile");

            if (File.Exists(dockerFilePath) && !Force)
            {
                throw new CommandException("'Dockerfile' already exists for project. use '--force' to overwrite.");
            }

            File.Delete(dockerFilePath);

            await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, dockerFilePath);

            output.WriteInfoLine($"Generated Dockerfile at '{dockerFilePath}'.");
        }
Пример #4
0
        public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service)
        {
            if (SkipWithoutProject(output, service, out var project))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var container))
            {
                return;
            }

            var chartDirectory = Path.Combine(application.GetProjectDirectory(project), "charts");

            if (Directory.Exists(chartDirectory) && !Force)
            {
                throw new CommandException("'charts' directory already exists for project. use '--force' to overwrite.");
            }
            else if (Directory.Exists(chartDirectory))
            {
                Directory.Delete(chartDirectory, recursive: true);
            }

            var chart = new HelmChartStep();
            await HelmChartGenerator.GenerateAsync(
                output,
                application,
                service,
                project,
                container,
                chart,
                new DirectoryInfo(chartDirectory));

            output.WriteInfoLine($"Generated Helm Chart at '{Path.Combine(chartDirectory, chart.ChartName)}'.");
        }
Пример #5
0
        public override Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service)
        {
            var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray();

            if (yaml.Length == 0)
            {
                output.WriteDebugLine($"No yaml manifests found for service '{service.FriendlyName}'. Skipping.");
                return(Task.CompletedTask);
            }

            var outputFilePath = Path.Combine(OutputDirectory.FullName, $"{service.Service.Name}.yaml");

            output.WriteInfoLine($"Writing output to '{outputFilePath}'.");
            if (File.Exists(outputFilePath) && !Force)
            {
                throw new CommandException($"'{service.Service.Name}.yaml' already exists for project. use '--force' to overwrite.");
            }

            File.Delete(outputFilePath);

            using var stream = File.OpenWrite(outputFilePath);
            using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);
            var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));

            yamlStream.Save(writer, assignAnchors: false);

            return(Task.CompletedTask);
        }
Пример #6
0
        private static async Task PackageApplicationAsync(OutputContext output, Application application, DirectoryInfo outputDirectory, string applicationName, string environment)
        {
            using var step = output.BeginStep("Writing Application Manifests...");
            var outputFile = Path.Combine(outputDirectory.FullName, $"{applicationName}-{environment}.yaml");

            output.WriteInfoLine($"Writing output to '{outputFile}'.");

            File.Delete(outputFile);
            using var stream = File.OpenWrite(outputFile);
            using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);

            if (application.Globals.DeploymentKind == DeploymentKind.None)
            {
                // No extra steps
            }
            else if (application.Globals.DeploymentKind == DeploymentKind.Kubernetes)
            {
                await ApplicationYamlWriter.WriteAsync(output, writer, application);
            }
            else if (application.Globals.DeploymentKind == DeploymentKind.Oam)
            {
                await OamApplicationGenerator.WriteOamApplicationAsync(writer, output, application, applicationName, environment);
            }
            else
            {
                throw new InvalidOperationException($"Unknown DeploymentKind: " + application.Globals.DeploymentKind);
            }

            step.MarkComplete();
        }
Пример #7
0
        public static async Task ReadProjectDetailsAsync(OutputContext output, FileInfo projectFile, Project project)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (projectFile is null)
            {
                throw new ArgumentNullException(nameof(projectFile));
            }

            if (project is null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            using (var step = output.BeginStep("Reading Project Details..."))
            {
                await EvaluateMSBuildAsync(output, projectFile, project);

                if (!SemVersion.TryParse(project.Version, out var version))
                {
                    output.WriteInfoLine($"No version or invalid version 'application.Version' found, using default.");
                    version         = new SemVersion(0, 1, 0);
                    project.Version = version.ToString();
                }

                step.MarkComplete();
            }
        }
Пример #8
0
        private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile)
        {
            var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName);

            if (config?.Container?.Registry?.Hostname == null)
            {
                throw new CommandException("a registry is required for push operations. run `dotnet-opulence init`");
            }

            var application = ApplicationFactory.CreateDefault(config, projectFile);
            await ProjectReader.InitializeAsync(output, application);

            await ScriptRunner.RunProjectScriptAsync(output, application);

            for (var i = 0; i < application.Steps.Count; i++)
            {
                var step = application.Steps[i];
                output.WriteInfoLine($"executing step: {step.DisplayName}");

                if (step is ContainerStep container)
                {
                    await DockerContainerBuilder.BuildContainerImageAsync(output, application, container);
                }
                else if (step is HelmChartStep chart)
                {
                    await HelmChartBuilder.BuildHelmChartAsync(output, application, application.Steps.Get <ContainerStep>() !, chart);
                }
            }

            {
                if (application.Steps.Get <ContainerStep>() is ContainerStep container)
                {
                    output.WriteInfoLine("pushing container");
                    await DockerPush.ExecuteAsync(output, container.ImageName !, container.ImageTag !);
                }

                if (application.Steps.Get <HelmChartStep>() is HelmChartStep chart)
                {
                    output.WriteInfoLine("pushing chart");
                    var chartFilePath = Path.Combine(application.ProjectDirectory, "bin", $"{chart.ChartName}-{application.Version.Replace('+', '-')}.tgz");
                    await HelmPush.ExecuteAsync(output, application.Config.Container !.Registry !.Hostname !, chartFilePath);
                }
            }
        }
Пример #9
0
        public static async Task RunProjectScriptAsync(OutputContext output, Application application)
        {
            var scriptFilePath = Path.ChangeExtension(application.ProjectFilePath, ".csx");

            output.WriteDebugLine($"looking for project script at {scriptFilePath}");
            if (!File.Exists(scriptFilePath))
            {
                output.WriteDebugLine($"no project script found");
                return;
            }

            output.WriteInfoLine($"configuring application using {Path.GetFileName(scriptFilePath)}");

            var code   = File.ReadAllText(scriptFilePath);
            var script = CSharpScript.Create <ConfigurationGlobals>(
                code,
                options: ScriptOptions.Default,
                globalsType: typeof(ConfigurationGlobals),
                assemblyLoader: null);

            script = script.ContinueWith <ConfigurationGlobals>(@"Package(App)", options: ScriptOptions.Default);

            output.WriteDebugLine("compiling project script");
            var diagnostics = script.Compile();

            if (diagnostics.Length > 0)
            {
                var builder = new StringBuilder();
                builder.AppendLine($"Script '{scriptFilePath}' had compilation errors.");
                foreach (var diagnostic in diagnostics)
                {
                    builder.AppendLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic));
                }

                throw new CommandException(builder.ToString());
            }
            output.WriteDebugLine("done compiling project script");

            var obj = new ConfigurationGlobals()
            {
                App = application,
            };

            output.WriteDebugLine("running project script");
            try
            {
                await script.RunAsync(obj);
            }
            catch (Exception ex)
            {
                throw new CommandException("failed executing project script", ex);
            }
            output.WriteDebugLine("done running project script");
        }
Пример #10
0
        private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile)
        {
            var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName);

            if (config == null)
            {
                // Allow operating without config for now.
                output.WriteInfoLine("config was not found, using defaults");
                config = new OpulenceConfig()
                {
                    Container = new ContainerConfig()
                    {
                        Registry = new RegistryConfig(),
                    }
                };
            }

            var application = ApplicationFactory.CreateDefault(config, projectFile);
            await ProjectReader.InitializeAsync(output, application);

            await ScriptRunner.RunProjectScriptAsync(output, application);

            for (var i = 0; i < application.Steps.Count; i++)
            {
                var step = application.Steps[i];
                output.WriteInfoLine($"executing step: {step.DisplayName}");

                if (step is ContainerStep container)
                {
                    await DockerContainerBuilder.BuildContainerImageAsync(output, application, container);
                }
                else if (step is HelmChartStep chart)
                {
                    await HelmChartBuilder.BuildHelmChartAsync(output, application, application.Steps.Get <ContainerStep>() !, chart);
                }
            }
        }
Пример #11
0
        public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service)
        {
            var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray();

            if (yaml.Length == 0)
            {
                output.WriteDebugLine($"No yaml manifests found for service '{service.FriendlyName}'. Skipping.");
                return;
            }

            using var tempFile = TempFile.Create();
            output.WriteDebugLine($"Writing output to '{tempFile.FilePath}'.");

            {
                using var stream = File.OpenWrite(tempFile.FilePath);
                using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);
                var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));
                yamlStream.Save(writer, assignAnchors: false);
            }

            // kubectl apply logic is implemented in the client in older versions of k8s. The capability
            // to get the same behavior in the server isn't present in every version that's relevant.
            //
            // https://kubernetes.io/docs/reference/using-api/api-concepts/#server-side-apply
            //
            output.WriteDebugLine("Running 'kubectl apply'.");
            output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"kubectl",
                $"apply -f \"{tempFile.FilePath}\"",
                System.Environment.CurrentDirectory,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'kubectl apply' failed.");
            }

            output.WriteInfoLine($"Deployed service '{service.FriendlyName}'.");
        }
Пример #12
0
            protected bool SkipWithoutProject(OutputContext output, ServiceEntry service, [MaybeNullWhen(returnValue: true)] out Project project)
            {
                if (output is null)
                {
                    throw new ArgumentNullException(nameof(output));
                }

                if (service is null)
                {
                    throw new ArgumentNullException(nameof(service));
                }

                if (service.Service.Source is Project p)
                {
                    project = p;
                    return(false);
                }

                output.WriteInfoLine($"Service '{service.FriendlyName}' does not have a project associated. Skipping.");
                project = default !;
Пример #13
0
        public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service)
        {
            if (SkipWithoutProject(output, service, out var _))
            {
                return;
            }

            if (SkipWithoutContainerInfo(output, service, out var _))
            {
                return;
            }

            if (SkipForEnvironment(output, service, Environment))
            {
                return;
            }

            foreach (var image in service.Outputs.OfType <DockerImageOutput>())
            {
                await DockerPush.ExecuteAsync(output, image.ImageName, image.ImageTag);

                output.WriteInfoLine($"Pushed docker image: '{image.ImageName}:{image.ImageTag}'");
            }
        }
Пример #14
0
        public static async Task InitializeAsync(OutputContext output, Application application)
        {
            output.WriteInfoLine("reading project information");

            try
            {
                output.WriteDebugLine("installing msbuild targets");
                TargetInstaller.Install(application.ProjectFilePath);
                output.WriteDebugLine("installed msbuild targets");
            }
            catch (Exception ex)
            {
                throw new CommandException("Failed to install targets.", ex);
            }

            var outputFilePath = Path.GetTempFileName();

            try
            {
                output.WriteDebugLine("executing dotnet msbuild");

                var capture      = output.Capture();
                var opulenceRoot = Path.GetDirectoryName(typeof(Program).Assembly.Location);
                var exitCode     = await Process.ExecuteAsync(
                    $"dotnet",
                    $"msbuild /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\"",
                    workingDir : application.ProjectDirectory,
                    stdOut : capture.StdOut,
                    stdErr : capture.StdErr);

                output.WriteDebugLine($"executed dotnet msbuild exit code: {exitCode}");
                if (exitCode != 0)
                {
                    throw new CommandException("Getting project info failed.");
                }

                var lines = await File.ReadAllLinesAsync(outputFilePath);

                for (var i = 0; i < lines.Length; i++)
                {
                    var line = lines[i];
                    if (line.StartsWith("version="))
                    {
                        application.Version = line.Substring("version=".Length).Trim();
                        output.WriteDebugLine($"found application version: {line}");
                        continue;
                    }

                    if (line.StartsWith("tfm"))
                    {
                        application.TargetFramework = line.Substring("tfm=".Length).Trim();
                        output.WriteDebugLine($"found target framework: {line}");
                        continue;
                    }

                    if (line.StartsWith("frameworks="))
                    {
                        var right = line.Substring("frameworks=".Length).Trim();
                        application.Frameworks.AddRange(right.Split(",").Select(s => new Framework(s)));
                        output.WriteDebugLine($"found shared frameworks: {line}");
                        continue;
                    }
                }
            }
            finally
            {
                File.Delete(outputFilePath);
            }

            output.WriteDebugLine("done reading project information");
        }
Пример #15
0
        public static async Task BuildContainerImageAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (application is null)
            {
                throw new ArgumentNullException(nameof(application));
            }

            if (service is null)
            {
                throw new ArgumentNullException(nameof(service));
            }

            if (project is null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (container is null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            using var tempFile = TempFile.Create();

            var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), Path.GetDirectoryName(project.RelativeFilePath) !, "Dockerfile");

            if (File.Exists(dockerFilePath))
            {
                output.WriteDebugLine($"Using existing dockerfile '{dockerFilePath}'.");
            }
            else
            {
                await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, tempFile.FilePath);

                dockerFilePath = tempFile.FilePath;
            }

            output.WriteDebugLine("Running 'docker build'.");
            output.WriteCommandLine("docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"docker",
                $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"",
                application.GetProjectDirectory(project),
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Done running 'docker build' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("'docker build' failed.");
            }

            output.WriteInfoLine($"Created Docker Image: {container.ImageName}:{container.ImageTag}");
            service.Outputs.Add(new DockerImageOutput(container.ImageName !, container.ImageTag !));
        }
Пример #16
0
        public static async Task <ApplicationWrapper?> RunCustomizationScriptAsync(OutputContext output, FileInfo solutionFile, SolutionFile solution)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (solutionFile is null)
            {
                throw new ArgumentNullException(nameof(solutionFile));
            }

            if (solution is null)
            {
                throw new ArgumentNullException(nameof(solution));
            }

            using (var step = output.BeginStep("Applying Application Customizations..."))
            {
                var scriptFilePath = Path.Combine(solutionFile.DirectoryName, "Opulence.csx");
                output.WriteDebugLine($"Looking for customization script at '{scriptFilePath}'.");
                if (!File.Exists(scriptFilePath))
                {
                    output.WriteDebugLine($"No customization script found.");
                    step.MarkComplete("Skipping...");
                    return(null);
                }

                output.WriteInfoLine($"Configuring application using '{Path.GetFileName(scriptFilePath)}'.");

                var code   = File.ReadAllText(scriptFilePath);
                var script = CSharpScript.Create <object>(
                    code,
                    options: ScriptOptions.Default.WithImports(new [] { "System", "System.Collections.Generic", }),
                    globalsType: typeof(PipelineHolder),
                    assemblyLoader: null);
                script = script.ContinueWith <object>(@"return await Pipeline.ExecuteAsync(__Pipeline);", options: ScriptOptions.Default);

                output.WriteDebugLine($"Compiling {Path.GetFileName(scriptFilePath)}'.");
                script.Compile();
                var diagnostics = script.Compile();
                if (diagnostics.Length > 0)
                {
                    var builder = new StringBuilder();
                    output.WriteDebugLine($"Script '{scriptFilePath}' had compilation errors.");
                    builder.AppendLine($"Script '{scriptFilePath}' had compilation errors.");
                    foreach (var diagnostic in diagnostics)
                    {
                        output.WriteDebugLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic));
                        builder.AppendLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic));
                    }

                    throw new CommandException(builder.ToString());
                }
                output.WriteDebugLine($"Done compiling {Path.GetFileName(scriptFilePath)}'.");

                var pipeline = new CustomizationPipeline(
                    output,
                    rootDirectory: Path.GetDirectoryName(scriptFilePath) !,
                    name: Names.NormalizeToDns(Path.GetFileNameWithoutExtension(solutionFile.Name)),
                    solution,
                    projectFile: null);
                var holder = new PipelineHolder(pipeline);

                output.WriteDebugLine($"Running {Path.GetFileName(scriptFilePath)}'.");
                object obj;
                try
                {
                    var result = await script.RunAsync(holder);

                    obj = result.ReturnValue;
                }
                catch (Exception ex)
                {
                    throw new CommandException($"Failed executing {Path.GetFileName(scriptFilePath)}'.", ex);
                }

                step.MarkComplete();
                return(new ApplicationWrapper(obj, Path.GetDirectoryName(scriptFilePath) !));
            }
        }
Пример #17
0
        public static async Task BuildHelmChartAsync(OutputContext output, Application application, ContainerStep container, HelmChartStep chart)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (application is null)
            {
                throw new ArgumentNullException(nameof(application));
            }

            if (container is null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            if (chart is null)
            {
                throw new ArgumentNullException(nameof(chart));
            }

            output.WriteInfoLine("building helm chart");

            var outputDirectoryPath = Path.Combine(application.ProjectDirectory, "bin");

            using var tempDirectory = TempDirectory.Create();

            HelmChartGenerator.ApplyHelmChartDefaults(application, container, chart);

            var chartRoot = Path.Combine(application.ProjectDirectory, "charts");
            var chartPath = Path.Combine(chartRoot, chart.ChartName);

            if (Directory.Exists(chartPath))
            {
                output.WriteDebugLine($"found existing chart in '{chartPath}'");
            }
            else
            {
                chartRoot = tempDirectory.DirectoryPath;
                chartPath = Path.Combine(chartRoot, chart.ChartName);
                output.WriteDebugLine($"generating chart in '{chartPath}");
                await HelmChartGenerator.GenerateAsync(output, application, container, chart, new DirectoryInfo(tempDirectory.DirectoryPath));
            }

            output.WriteDebugLine("running helm package");
            output.WriteDebugLine($"> helm package -d \"{outputDirectoryPath}\" --version {application.Version.Replace('+', '-')} --app-version {application.Version.Replace('+', '-')}");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                "helm",
                $"package . -d \"{outputDirectoryPath}\" --version {application.Version.Replace('+', '-')} --app-version {application.Version.Replace('+', '-')}",
                workingDir : chartPath,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"running helm package exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("helm package failed");
            }

            output.WriteDebugLine("done building helm chart");
        }
Пример #18
0
        private static async Task ExecuteAsync(OutputContext output, DirectoryInfo directory)
        {
            output.WriteBanner();

            string?solutionFilePath = null;
            string?opulenceFilePath = null;

            using (var step = output.BeginStep("Looking For Existing Config..."))
            {
                opulenceFilePath = DirectorySearch.AscendingSearch(directory.FullName, "Opulence.csx");
                if (opulenceFilePath != null)
                {
                    output.WriteInfoLine($"Found 'Opulence.csx' at '{opulenceFilePath}'");
                    step.MarkComplete();
                    return;
                }
                else
                {
                    output.WriteInfoLine("Not Found");
                    step.MarkComplete();
                }
            }

            using (var step = output.BeginStep("Looking For .sln File..."))
            {
                solutionFilePath = DirectorySearch.AscendingWildcardSearch(directory.FullName, "*.sln").FirstOrDefault()?.FullName;
                if (opulenceFilePath == null &&
                    solutionFilePath != null &&
                    output.Confirm($"Use '{Path.GetDirectoryName(solutionFilePath)}' as Root?"))
                {
                    opulenceFilePath = Path.Combine(Path.GetDirectoryName(solutionFilePath) !, "Opulence.csx");
                    step.MarkComplete();
                }
                else
                {
                    output.WriteInfoLine("Not Found.");
                    step.MarkComplete();
                }
            }

            if (opulenceFilePath == null &&
                Path.GetFullPath(directory.FullName) != Path.GetFullPath(Environment.CurrentDirectory))
            {
                // User specified a directory other than the current one
                using (var step = output.BeginStep("Trying Project Directory..."))
                {
                    if (output.Confirm("Use Project Directory as Root?"))
                    {
                        opulenceFilePath = Path.Combine(directory.FullName, "Opulence.csx");
                    }

                    step.MarkComplete();
                }
            }

            if (opulenceFilePath == null)
            {
                using (var step = output.BeginStep("Trying Current Directory..."))
                {
                    if (output.Confirm("Use Current Directory as Root?"))
                    {
                        opulenceFilePath = Path.Combine(directory.FullName, "Opulence.csx");
                    }

                    step.MarkComplete();
                }
            }

            if (opulenceFilePath == null)
            {
                throw new CommandException("Cannot Determine Root Directory.");
            }

            using (var step = output.BeginStep("Writing Opulence.csx ..."))
            {
                var hostname = output.Prompt("Enter the Container Registry (ex: 'example.azurecr.io' for Azure or 'example' for dockerhub)");

                var services = new List <(string, string)>();
                if (solutionFilePath != null && output.Confirm($"Use solution file '{solutionFilePath}' to initialize services?"))
                {
                    services.AddRange(ReadServicesFromSolution(solutionFilePath));
                    services.Sort((a, b) => a.Item1.CompareTo(b.Item1));
                }

                using var stream = File.OpenWrite(opulenceFilePath);
                using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true);

                await WriteOpulenceConfigAsync(writer, hostname, services);

                output.WriteInfoLine($"Initialized Opulence Config at '{opulenceFilePath}'.");
                step.MarkComplete();
            }
        }
Пример #19
0
        public static async Task BuildHelmChartAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, HelmChartStep chart)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            if (application is null)
            {
                throw new ArgumentNullException(nameof(application));
            }

            if (service is null)
            {
                throw new ArgumentNullException(nameof(service));
            }

            if (project is null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (container is null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            if (chart is null)
            {
                throw new ArgumentNullException(nameof(chart));
            }

            var projectDirectory    = Path.Combine(application.RootDirectory, Path.GetDirectoryName(project.RelativeFilePath) !);
            var outputDirectoryPath = Path.Combine(projectDirectory, "bin");

            using var tempDirectory = TempDirectory.Create();

            HelmChartGenerator.ApplyHelmChartDefaults(application, service, container, chart);

            var chartRoot = Path.Combine(projectDirectory, "charts");
            var chartPath = Path.Combine(chartRoot, chart.ChartName);

            output.WriteDebugLine($"Looking for existing chart in '{chartPath}'.");
            if (Directory.Exists(chartPath))
            {
                output.WriteDebugLine($"Found existing chart in '{chartPath}'.");
            }
            else
            {
                chartRoot = tempDirectory.DirectoryPath;
                chartPath = Path.Combine(chartRoot, chart.ChartName);
                output.WriteDebugLine($"Generating chart in '{chartPath}'.");
                await HelmChartGenerator.GenerateAsync(output, application, service, project, container, chart, new DirectoryInfo(tempDirectory.DirectoryPath));
            }

            output.WriteDebugLine("Running 'helm package'.");
            output.WriteCommandLine("helm", $"package -d \"{outputDirectoryPath}\" --version {project.Version.Replace('+', '-')} --app-version {project.Version.Replace('+', '-')}");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                "helm",
                $"package . -d \"{outputDirectoryPath}\" --version {project.Version.Replace('+', '-')} --app-version {project.Version.Replace('+', '-')}",
                workingDir : chartPath,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"Running 'helm package' exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("Running 'helm package' failed.");
            }

            output.WriteInfoLine($"Created Helm Chart: {Path.Combine(outputDirectoryPath, chart.ChartName + "-" + project.Version.Replace('+', '-') + ".tgz")}");
        }
Пример #20
0
        private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile, List <string> outputs, bool force)
        {
            var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName);

            if (config == null)
            {
                // Allow operating without config for now.
                output.WriteInfoLine("config was not found, using defaults");
                config = new OpulenceConfig()
                {
                    Container = new ContainerConfig()
                    {
                        Registry = new RegistryConfig(),
                    }
                };
            }

            var application = ApplicationFactory.CreateDefault(config, projectFile);
            await ProjectReader.InitializeAsync(output, application);

            await ScriptRunner.RunProjectScriptAsync(output, application);

            for (var i = 0; i < application.Steps.Count; i++)
            {
                var step = application.Steps[i];

                if (step is ContainerStep container)
                {
                    if (!outputs.Contains("container"))
                    {
                        // We should still apply the defaults here because they'll be used by
                        // the helm step.
                        DockerfileGenerator.ApplyContainerDefaults(application, container);

                        output.WriteDebugLine("skipping container");
                        continue;
                    }

                    output.WriteInfoLine("generating dockerfile");

                    var dockerFilePath = Path.Combine(application.ProjectDirectory, "Dockerfile");
                    if (File.Exists(dockerFilePath) && !force)
                    {
                        throw new CommandException("'Dockerfile' already exists for project. use --force to overwrite");
                    }

                    // force multi-phase dockerfile - this makes much more sense in the workflow
                    // where you're going to maintain the dockerfile yourself.
                    container.UseMultiphaseDockerfile = true;

                    File.Delete(dockerFilePath);

                    await DockerfileGenerator.WriteDockerfileAsync(output, application, container, dockerFilePath);
                }
                else if (step is HelmChartStep chart)
                {
                    if (!outputs.Contains("chart"))
                    {
                        output.WriteDebugLine("skipping helm chart");
                        continue;
                    }

                    output.WriteInfoLine("generating helm charts");

                    var chartDirectory = Path.Combine(application.ProjectDirectory, "charts");
                    if (Directory.Exists(chartDirectory) && !force)
                    {
                        throw new CommandException("'charts' directory already exists for project. use --force to overwrite");
                    }

                    await HelmChartGenerator.GenerateAsync(
                        output,
                        application,
                        application.Steps.Get <ContainerStep>() !,
                        chart,
                        new DirectoryInfo(chartDirectory));
                }
            }
        }