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}'.");
        }
Beispiel #2
0
        private static async Task <Application> InferApplicationForProject(OutputContext output, FileInfo projectFile)
        {
            var globals = new ApplicationGlobals();

            globals.Name = Names.NormalizeToDns(Path.GetFileNameWithoutExtension(projectFile.Name));

            var services = new List <ServiceEntry>();

            var name    = Path.GetFileNameWithoutExtension(projectFile.Name);
            var project = new Project(projectFile.FullName);
            var service = new ServiceEntry(new Service(name), name);

            service.Service.Source = project;
            services.Add(service);

            await ProjectReader.ReadProjectDetailsAsync(output, projectFile, project);

            var container = new ContainerInfo();

            service.Service.GeneratedAssets.Container = container;

            var application = new GroveledApplication(globals, projectFile.DirectoryName, services);

            DockerfileGenerator.ApplyContainerDefaults(application, service, project, container);

            return(application);
        }
Beispiel #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.");
            }
        }
Beispiel #4
0
        private static async Task <Application> InferApplicationForSolution(OutputContext output, FileInfo solutionFile, SolutionFile solution)
        {
            var globals = new ApplicationGlobals();

            globals.Name = Names.NormalizeToDns(Path.GetFileNameWithoutExtension(solutionFile.Name));

            var services = new List <ServiceEntry>();

            for (var i = 0; i < solution.ProjectsInOrder.Count; i++)
            {
                // The library we're using doesn't translate Windows style paths automatically.
                var solutionProject = solution.ProjectsInOrder[i];
                if (solutionProject.AbsolutePath.EndsWith(".csproj", StringComparison.Ordinal))
                {
                    var projectFilePath = solutionProject.RelativePath.Replace('\\', Path.DirectorySeparatorChar);
                    var projectFile     = new FileInfo(projectFilePath);

                    var name    = Path.GetFileNameWithoutExtension(projectFile.Name);
                    var project = new Project(projectFile.FullName);
                    var service = new ServiceEntry(new Service(name), name);
                    service.Service.Source = project;
                    services.Add(service);

                    await ProjectReader.ReadProjectDetailsAsync(output, projectFile, project);

                    var container = new ContainerInfo();
                    service.Service.GeneratedAssets.Container = container;
                }
            }

            var application = new GroveledApplication(globals, solutionFile.DirectoryName, services);

            foreach (var service in application.Services)
            {
                DockerfileGenerator.ApplyContainerDefaults(application, service, (Project)service.Service.Source !, service.Service.GeneratedAssets.Container !);
            }

            return(application);
        }
Beispiel #5
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 !));
        }
        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);
        }
        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));
                }
            }
        }