Example #1
0
        public static async Task ExecuteAsync(OutputContext output, string imageName, string imageTag)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

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

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

            output.WriteDebugLine("running docker push");
            output.WriteDebugLine($"> docker push {imageName}:{imageTag}");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"docker",
                $"push {imageName}:{imageTag}",
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"done running docker push exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("docker push failed");
            }
        }
        public static async Task <OpulenceConfig?> ReadConfigAsync(OutputContext output, string directoryPath)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

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

            output.WriteDebugLine($"searching for opulence.json above '{directoryPath}'");

            var configFilePath = DirectorySearch.AscendingSearch(directoryPath, "opulence.json");

            if (configFilePath == null)
            {
                output.WriteDebugLine("no configuration found");
                return(null);
            }

            output.WriteDebugLine($"configuration found at '{configFilePath}'");

            using var stream = File.OpenRead(configFilePath);
            return(await JsonSerializer.DeserializeAsync <OpulenceConfig>(stream));
        }
Example #3
0
        public static async Task BuildContainerImageAsync(OutputContext output, Application application, ContainerStep container)
        {
            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));
            }

            DockerfileGenerator.ApplyContainerDefaults(application, container);

            using var tempFile = TempFile.Create();

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

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

                dockerFilePath = tempFile.FilePath;
            }

            output.WriteDebugLine("running docker build");
            output.WriteDebug($"> 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.ProjectDirectory,
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"done running docker build exit code:{exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("Docker build failed.");
            }
        }
Example #4
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();
        }
Example #5
0
        public static async Task WriteDockerfileAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, string filePath)
        {
            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 (filePath is null)
            {
                throw new ArgumentNullException(nameof(filePath));
            }

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

            var entryPoint = Path.GetFileNameWithoutExtension(project.RelativeFilePath);

            output.WriteDebugLine($"Writing dockerfile to '{filePath}'.");
            if (container.UseMultiphaseDockerfile ?? true)
            {
                await WriteMultiphaseDockerfileAsync(writer, entryPoint, container);
            }
            else
            {
                await WriteLocalPublishDockerfileAsync(writer, entryPoint, container);
            }
            output.WriteDebugLine("Done writing dockerfile.");
        }
Example #6
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);
        }
Example #7
0
            protected bool SkipForEnvironment(OutputContext output, ServiceEntry service, string environment)
            {
                if (!service.AppliesToEnvironment(environment))
                {
                    output.WriteDebugLine($"Service '{service.FriendlyName}' is not part of environment '{environment}'. Skipping.");
                    return(true);
                }

                return(false);
            }
        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}'.");
        }
Example #9
0
        public static async Task ExecuteAsync(OutputContext output, string registryHostname, string chartFilePath)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

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

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

            // NOTE: this is currently hardcoded to use ACR and uses the azure CLI to do
            // the push operation. Helm 3 has support for pushing to OCI repositories,
            // but it's not documented that it works yet for azure.
            if (!registryHostname.EndsWith(".azurecr.io"))
            {
                throw new CommandException("currently pushing of helm charts is limited to '*.azurecr.io'");
            }

            var registryName = registryHostname.Substring(0, registryHostname.Length - ".azurecr.io".Length);

            output.WriteDebugLine("running az acr helm push");
            output.WriteDebugLine($"> az acr helm push --name {registryName} \"{chartFilePath}\"");
            var capture  = output.Capture();
            var exitCode = await Process.ExecuteAsync(
                $"az",
                $"acr helm push --name {registryName} \"{chartFilePath}\"",
                stdOut : capture.StdOut,
                stdErr : capture.StdErr);

            output.WriteDebugLine($"done running az acr helm push exit code: {exitCode}");
            if (exitCode != 0)
            {
                throw new CommandException("az acr helm push failed");
            }
        }
        public static async Task WriteDockerfileAsync(OutputContext output, Application application, ContainerStep container, string filePath)
        {
            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 (filePath is null)
            {
                throw new ArgumentNullException(nameof(filePath));
            }

            ApplyContainerDefaults(application, container);

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

            output.WriteDebugLine($"writing dockerfile to '{filePath}'");
            if (container.UseMultiphaseDockerfile ?? true)
            {
                await WriteMultiphaseDockerfileAsync(writer, application, container);
            }
            else
            {
                await WriteLocalPublishDockerfileAsync(writer, application, container);
            }
            output.WriteDebugLine("done writing dockerfile");
        }
Example #11
0
        public static async Task <Application> CreateApplicationAsync(OutputContext output, FileInfo projectFile)
        {
            if (output is null)
            {
                throw new ArgumentNullException(nameof(output));
            }

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

            if (string.Equals(projectFile.Extension, ".sln", StringComparison.Ordinal))
            {
                output.WriteDebugLine($"Solution '{projectFile.FullName}' was provided as input.");
                return(await CreateApplicationForSolutionAsync(output, projectFile));
            }
            else
            {
                output.WriteDebugLine($"Project '{projectFile.FullName}' was provided as input.");
                return(await CreateApplicationForProjectAsync(output, projectFile));
            }
        }
Example #12
0
        public static Task WriteAsync(OutputContext output, StreamWriter writer, Application application)
        {
            var yaml = application.Services.SelectMany(s => s.Outputs.OfType <IYamlManifestOutput>()).ToArray();

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

            var yamlStream = new YamlStream(yaml.Select(y => y.Yaml));

            yamlStream.Save(writer, assignAnchors: false);

            return(Task.CompletedTask);
        }
Example #13
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");
        }
Example #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");
        }
Example #15
0
        public override async Task <object?> ExecuteAsync()
        {
            if (callbacks.Count == 0)
            {
                return(Task.FromResult <object?>(null));
            }

            if (callbacks.Count > 1)
            {
                throw new InvalidOperationException("More than one application type is not supported.");
            }

            var kvp = callbacks.Single();

            var type      = kvp.Key;
            var delegates = kvp.Value;

            output.WriteDebugLine($"Creating instance of application type '{type}'.");
            var application = Activator.CreateInstance(type);

            output.WriteDebugLine($"Done creating instance of application type '{type}'.");

            var wrapper = new ApplicationWrapper(application !, rootDirectory);

            wrapper.Globals.Name ??= name;

            foreach (var service in wrapper.Services)
            {
                output.WriteDebugLine($"Found service '{service.FriendlyName} {{ Name: {service.Service.Name} }}'.");

                string?projectRelativeFilePath = null;
                string?projectFilePath         = null;
                if (solution != null)
                {
                    var project = FindProjectInSolution(solution, service.FriendlyName);
                    if (project == null)
                    {
                        output.WriteDebugLine($"Could not find project for service '{service.FriendlyName}'.");
                        continue;
                    }

                    output.WriteDebugLine($"Found project '{project.RelativePath}' for service '{service.FriendlyName}'.");
                    projectRelativeFilePath = project.RelativePath.Replace('\\', Path.DirectorySeparatorChar);
                    projectFilePath         = project.AbsolutePath.Replace('\\', Path.DirectorySeparatorChar);
                }
                else if (projectFile != null)
                {
                    var normalized = Names.NormalizeToFriendly(Path.GetFileNameWithoutExtension(projectFile.Name));
                    if (!string.Equals(normalized, service.FriendlyName))
                    {
                        output.WriteDebugLine($"Skipping service '{service.FriendlyName}'.");
                        continue;
                    }

                    projectRelativeFilePath = projectFile.FullName;
                    projectFilePath         = projectFile.FullName;
                }

                if (projectFilePath != null)
                {
                    var project = new Project(projectRelativeFilePath !);
                    await ProjectReader.ReadProjectDetailsAsync(output, new FileInfo(projectFilePath), project);

                    service.Service.Source = project;

                    // Apply defaults to everything that has a project.
                    var container = new ContainerInfo();
                    service.Service.GeneratedAssets.Container = container;
                    DockerfileGenerator.ApplyContainerDefaults(wrapper, service, project, container);
                }
            }

            output.WriteDebugLine($"Running {delegates.Count} customization callbacks.");
            for (var i = 0; i < delegates.Count; i++)
            {
                delegates[i].DynamicInvoke(application);
            }
            output.WriteDebugLine($"Done running {delegates.Count} customization callbacks.");


            return(application);
        }
Example #16
0
        public static async Task GenerateAsync(OutputContext output, Application application, ContainerStep container, HelmChartStep chart, DirectoryInfo outputDirectory)
        {
            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));
            }

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

            output.WriteDebugLine("generating helm chart");

            ApplyHelmChartDefaults(application, container, chart);

            // The directory with the charts needs to be the same as the chart name
            var chartDirectoryPath = Path.Combine(outputDirectory.FullName, chart.ChartName);

            Directory.CreateDirectory(chartDirectoryPath);

            var templateDirectoryPath = Path.Combine(
                Path.GetDirectoryName(typeof(HelmChartGenerator).Assembly.Location) !,
                "Templates",
                "Helm");

            DirectoryCopy.Copy(templateDirectoryPath, chartDirectoryPath);

            // Write Chart.yaml
            //
            // apiVersion: v1
            // name: <appname>
            // version: <version>
            // appVersion: <version>
            await File.WriteAllLinesAsync(Path.Combine(chartDirectoryPath, "Chart.yaml"), new[]
            {
                $"apiVersion: v1",
                $"name: {chart.ChartName}",
                $"# helm requires the version and appVersion to specified in Chart.yaml",
                $"# opulence will override these values when packaging the chart",
                $"version: {application.Version.Replace('+', '-')}",
                $"appVersion: {application.Version.Replace('+', '-')}"
            });

            // Write values.yaml
            //
            // image:
            //   repository: rynowak.azurecr.io/rochambot/gamemaster
            await File.WriteAllLinesAsync(Path.Combine(chartDirectoryPath, "values.yaml"), new[]
            {
                $"image:",
                $"  repository: {container.ImageName}",
            });

            output.WriteDebugLine("done generating helm chart");
        }
Example #17
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 !));
        }
Example #18
0
        private static async Task EvaluateMSBuildAsync(OutputContext output, FileInfo projectFile, Project project)
        {
            try
            {
                output.WriteDebugLine("Installing msbuild targets.");
                TargetInstaller.Install(projectFile.FullName);
                output.WriteDebugLine("Installed msbuild targets.");
            }
            catch (Exception ex)
            {
                throw new CommandException("Failed to install targets.", ex);
            }

            var outputFilePath = Path.GetTempFileName();

            try
            {
                var capture      = output.Capture();
                var opulenceRoot = Path.GetDirectoryName(typeof(Program).Assembly.Location);

                var restore = string.Empty;
                if (!File.Exists(Path.Combine(projectFile.DirectoryName, "obj", "project.assets.json")))
                {
                    restore = "/restore";
                }

                output.WriteDebugLine("Running 'dotnet msbuild'.");
                output.WriteCommandLine("dotnet", $"msbuild {restore} /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\"");
                var exitCode = await Process.ExecuteAsync(
                    $"dotnet",
                    $"msbuild {restore} /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\"",
                    workingDir : projectFile.DirectoryName,
                    stdOut : capture.StdOut,
                    stdErr : capture.StdErr);

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

                var lines = await File.ReadAllLinesAsync(outputFilePath);

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

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

                    if (line.StartsWith("frameworks="))
                    {
                        var right = line.Substring("frameworks=".Length).Trim();
                        project.Frameworks.AddRange(right.Split(",").Select(s => new Framework(s)));
                        output.WriteDebugLine($"Found shared frameworks: {line}");
                        continue;
                    }
                }
            }
            finally
            {
                File.Delete(outputFilePath);
            }
        }
Example #19
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) !));
            }
        }
Example #20
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");
        }
Example #21
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));
                }
            }
        }
Example #22
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")}");
        }