proof of concept for helm packaging & deployment

This commit is contained in:
Chris
2025-11-16 18:35:24 -08:00
parent e97a81b15d
commit 4cef7d944f
6 changed files with 143 additions and 6 deletions
+77
View File
@@ -0,0 +1,77 @@
class Projects::HelmDeploymentJob < ApplicationJob
DEPLOYABLE_RESOURCES = %w[ConfigMap Secrets Deployment CronJob Service Ingress Pv Pvc]
def perform(deployment, user)
project = deployment.project
connection = K8::Connection.new(project, user, allow_anonymous: true)
chart_builder = K8::Helm::ChartBuilder.new(
project.name,
deployment,
).connect(
K8::Connection.new(project, user, allow_anonymous: true)
)
#chart_builder << apply_namespace(project)
chart_builder << upload_registry_secrets(deployment)
chart_builder << apply_config_map(project)
chart_builder << apply_secrets(project)
deploy_volumes(project, chart_builder)
#chart_builder << predeploy(project, connection)
deploy_services(project, chart_builder)
chart_builder.install_chart(project.name)
end
def apply_namespace(project)
K8::Namespace.new(project)
end
def upload_registry_secrets(deployment)
project = deployment.project
provider = project.build_provider
result = Providers::GenerateConfigJson.execute(
provider:,
)
raise StandardError, result.message if result.failure?
K8::Secrets::RegistrySecret.new(project, result.docker_config_json)
end
DEPLOYABLE_RESOURCES.each do |resource_type|
define_method(:"apply_#{resource_type.underscore}") do |service|
K8::Stateless.const_get(resource_type).new(service)
end
end
def deploy_volumes(project, chart_builder)
project.volumes.each do |volume|
begin
chart_builder << apply_pv(volume)
chart_builder << apply_pvc(volume)
volume.deployed!
rescue StandardError => e
volume.failed!
raise e
end
end
end
def deploy_services(project, chart_builder)
project.services.each do |service|
deploy_service(service, chart_builder)
end
end
def deploy_service(service, chart_builder)
if service.background_service?
chart_builder << apply_deployment(service)
elsif service.cron_job?
chart_builder << apply_cron_job(service)
elsif service.web_service?
chart_builder << apply_deployment(service)
chart_builder << apply_service(service)
if service.domains.any? && service.allow_public_networking?
chart_builder << apply_ingress(service)
end
end
end
end
+1 -4
View File
@@ -27,10 +27,7 @@ class Deployment < ApplicationRecord
end
def add_manifest(yaml)
manifest = YAML.safe_load(yaml)
kind = manifest["kind"]&.downcase
name = manifest.dig("metadata", "name")
manifest_key = "#{kind}/#{name}"
manifest_key = K8::Base.manifest_key(yaml)
self.manifests ||= {}
self.manifests[manifest_key] = yaml
+11
View File
@@ -29,6 +29,17 @@ class K8::Base
result.gsub(/\n\s*\n/, "\n")
end
def self.manifest_key(yaml)
manifest = YAML.safe_load(yaml)
kind = manifest["kind"]&.downcase
name = manifest.dig("metadata", "name")
manifest_key = "#{kind}/#{name}"
end
def suggested_file_name
"#{K8::Base.manifest_key(to_yaml).gsub("/", "_").underscore}.yaml"
end
def client
raise "Client not connected" unless connected?
@client ||= K8::Client.new(connection)
+47
View File
@@ -0,0 +1,47 @@
class K8::Helm::ChartBuilder < K8::Base
attr_reader :chart_name, :resources, :logger, :client
def initialize(chart_name, logger)
@chart_name = chart_name
@logger = logger
@resources = []
end
def connect(connection)
@client = K8::Helm::Client.connect(connection, Cli::RunAndLog.new(logger))
super(connection)
end
def <<(resource)
resources << resource
end
def chart_yaml
<<-YAML
apiVersion: v2
name: <%= chart_name %>
version: 1.0.0
type: application
appVersion: "1.0.0"
YAML
end
def install_chart(namespace)
Dir.mktmpdir do |chart_directory|
logger.info("Creating chart directory #{chart_directory}...")
# Create /templates directory
FileUtils.mkdir_p(File.join(chart_directory, "templates"))
logger.info("Creating Chart.yaml")
File.write(File.join(chart_directory, "Chart.yaml"), chart_yaml)
resources.each do |resource|
logger.info("Writing template #{resource.suggested_file_name}...")
File.write(File.join(chart_directory, "templates", resource.suggested_file_name), resource.to_yaml)
end
logger.info("Installing chart #{chart_name} in namespace #{namespace}...")
client.install(chart_name, chart_directory, namespace: namespace)
end
end
end
+2 -2
View File
@@ -78,7 +78,7 @@ class K8::Helm::Client
self.class.add_repo(repository_name, repository_url, runner)
end
def install(name, chart_url, values: {}, namespace: 'default')
def install(name, chart_url, values: {}, namespace: 'default', dry_run: false)
return StandardError.new("Can't install helm chart if not connected") unless connected?
with_kube_config do |kubeconfig_file|
@@ -88,7 +88,7 @@ class K8::Helm::Client
values_file.write(values.to_yaml)
values_file.flush
command = "helm upgrade --install #{name} #{chart_url} -f #{values_file.path} --namespace #{namespace} --timeout=#{DEFAULT_TIMEOUT}"
command = "helm upgrade --install #{name} #{chart_url} -f #{values_file.path} --namespace #{namespace} --timeout=#{DEFAULT_TIMEOUT} #{dry_run ? '--dry-run' : ''}"
exit_status = runner.(command, envs: { "KUBECONFIG" => kubeconfig_file.path })
raise "`#{command}` failed with exit status #{exit_status}" unless exit_status.success?
exit_status
+5
View File
@@ -0,0 +1,5 @@
apiVersion: v2
name: <%= project.name %>
version: 1.0.0
type: application
appVersion: "1.0.0"