mirror of
https://github.com/czhu12/canine.git
synced 2026-05-06 19:59:51 -05:00
proof of concept for helm packaging & deployment
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: <%= project.name %>
|
||||
version: 1.0.0
|
||||
type: application
|
||||
appVersion: "1.0.0"
|
||||
Reference in New Issue
Block a user