added predeploy command

This commit is contained in:
Chris Zhu
2025-03-14 21:07:28 -07:00
parent 0063766962
commit dcca8954d7
7 changed files with 116 additions and 14 deletions

View File

@@ -12,11 +12,7 @@
- [ ] Show ingress logs at the cluster level -- parse NGINX logs
- [ ] Streaming logs for pods
- [ ] Project groupings?
- [ ] Validate kubeconfig file
- [ ] Create a pricing calculator
<<<<<<< Updated upstream
- [ ] Constantly refresh the processes page for readiness of pods
=======
- [ ] Pre-deploy task
- [ ] Metrics improvement: https://stackoverflow.com/questions/68058199/chartjs-need-help-on-drawing-a-vertical-line-when-hovering-cursor
>>>>>>> Stashed changes

View File

@@ -12,7 +12,8 @@ module Projects
:docker_build_context_directory,
:docker_command,
:dockerfile_path,
:container_registry_url
:container_registry_url,
:predeploy_command,
)
end

View File

@@ -20,7 +20,7 @@ class Projects::DeploymentJob < ApplicationJob
apply_config_map(project, kubectl)
deploy_volumes(project, kubectl)
predeploy(project, deployment)
predeploy(project, kubectl)
# For each of the projects services
deploy_services(project, kubectl)
@@ -53,15 +53,17 @@ class Projects::DeploymentJob < ApplicationJob
end
end
def predeploy(project, deployment)
def predeploy(project, kubectl)
return unless project.predeploy_command.present?
@logger.info("Running predeploy command: #{project.predeploy_command}", color: :yellow)
success = system(project.predeploy_command)
@logger.info("Running predeploy command: `#{project.predeploy_command}`...", color: :yellow)
command = K8::Stateless::Command.new(project)
command_yaml = command.to_yaml
command.delete_if_exists!
kubectl.apply_yaml(command_yaml)
return if success
raise DeploymentFailure, "Predeploy command failed for project #{project.name}"
# Wait for the predeploy to finish
command.wait_for_completion
end

View File

@@ -16,7 +16,6 @@ module Cli
def call(command, envs: {})
command = envs.map { |k, v| "#{k}=#{v}" }.join(" ") + " #{command}"
@loggable.info(command.strip)
Open3.popen3(command.strip) do |stdin, stdout, stderr, wait_thr|
stdin.close
stdout.each_line { |line| @loggable.info(line.chomp) }

View File

@@ -0,0 +1,61 @@
class K8::Stateless::Command < K8::Base
attr_accessor :project
def initialize(project)
@project = project
end
def kubectl
@kubectl ||= K8::Kubectl.new(project.cluster.kubeconfig)
end
def command
project.predeploy_command
end
def name
"#{project.name}-predeployment"
end
def namespace
project.name
end
def delete_if_exists!
return if kubectl.call("get job #{name} -n #{namespace}").strip.empty?
kubectl.call("delete job #{name} -n #{namespace}")
rescue StandardError => e
nil
end
def wait_for_completion
retries = 0
while true
break if done?
sleep(3.0)
retries += 1
if retries > 30
raise Projects::DeploymentJob::DeploymentFailure, "Predeploy command `#{command}` took too long to complete"
end
end
end
def done?
_statuses = statuses
if _statuses.include?("Failed") || _statuses.include?("ActiveDeadlineExceeded")
raise Projects::DeploymentJob::DeploymentFailure, "Predeploy command `#{command}` failed"
end
_statuses.include?("Complete")
end
def statuses
begin
output = kubectl.call("get job #{name} -n #{namespace} -o jsonpath='{.status.conditions[*].type}'")
conditions = output.split
conditions.map { |condition| condition.strip }
rescue => e
Rails.logger.error("Error checking job completion: #{e.message}")
false
end
end
end

View File

@@ -1,5 +1,5 @@
<%= turbo_frame_tag "new_service" do %>
<div class="overflow-x-auto">
<div>
<%= form_with(model: [@project, @service], url: project_services_path) do |form| %>
<div class="form-group">
<%= form.label :name %>

View File

@@ -0,0 +1,43 @@
apiVersion: batch/v1
kind: Job
metadata:
name: <%= name %>
namespace: <%= project.name %>
labels:
caninemanaged: 'true'
app: <%= project.name %>
component: predeployment
spec:
backoffLimit: 1
template:
metadata:
labels:
app: <%= project.name %>
component: predeployment
spec:
restartPolicy: Never
containers:
- name: <%= project.name %>
image: <%= project.container_registry_url %>
imagePullPolicy: Always
command: <%= command.split.to_json %>
envFrom:
- configMapRef:
name: <%= project.name %>
<% if @project.volumes.present? %>
volumeMounts:
<% @project.volumes.each do |volume| %>
- name: <%= volume.name %>
mountPath: <%= volume.mount_path %>
<% end %>
<% end %>
imagePullSecrets:
- name: dockerconfigjson-github-com
<% if @project.volumes.present? %>
volumes:
<% project.volumes.each do |volume| %>
- name: <%= volume.name %>
persistentVolumeClaim:
claimName: <%= volume.name %>
<% end %>
<% end %>