mirror of
https://github.com/czhu12/canine.git
synced 2025-12-16 16:35:10 -06:00
added processes
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
- [ ] Onboarding flow
|
||||
- [ ] we should have a feature to continuously poll stuff and figure out if they are still alive
|
||||
- [ ] Healthchecks and whatnot
|
||||
- [ ] Command to actually open a console with all the environment variables set up
|
||||
- [ ] Finish the landing page
|
||||
- [ ] Write the manifesto
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
@import "./forms.css";
|
||||
@import "./weird_fixes.css";
|
||||
@import "./tables.css";
|
||||
@import "pacman.css";
|
||||
@import "pacman.css";
|
||||
@import "custom.css";
|
||||
3
app/assets/stylesheets/custom.css
Normal file
3
app/assets/stylesheets/custom.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.help-text {
|
||||
@apply text-sm text-base-content/80 italic;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
class AddOns::LogsController < AddOns::BaseController
|
||||
include LogColorsHelper
|
||||
|
||||
def index
|
||||
@pods = get_pods_for_add_on(@add_on)
|
||||
end
|
||||
|
||||
@@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
before_action :configure_permitted_parameters, if: :devise_controller?
|
||||
before_action :authenticate_user!
|
||||
|
||||
layout :determine_layout
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
class Projects::LogsController < Projects::BaseController
|
||||
include LogColorsHelper
|
||||
def index
|
||||
@pods = get_pods_for_project(@project)
|
||||
end
|
||||
|
||||
def show
|
||||
client = K8::Client.new(@project.cluster.kubeconfig)
|
||||
@logs = client.get_pod_log(params[:id], @project.name)
|
||||
end
|
||||
|
||||
private
|
||||
def get_pods_for_project(project)
|
||||
# Get all pods for a given namespace
|
||||
client = K8::Client.new(project.cluster.kubeconfig).client
|
||||
pods = client.get_pods(namespace: project.name)
|
||||
end
|
||||
|
||||
def set_cluster
|
||||
@cluster = current_user.clusters.find(params[:cluster_id])
|
||||
end
|
||||
end
|
||||
36
app/controllers/projects/processes_controller.rb
Normal file
36
app/controllers/projects/processes_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
class Projects::ProcessesController < Projects::BaseController
|
||||
include LogColorsHelper
|
||||
|
||||
def index
|
||||
@pods = get_pods_for_project(@project)
|
||||
end
|
||||
|
||||
def create
|
||||
kubectl = K8::Kubectl.from_project(@project)
|
||||
pod = K8::Stateless::Pod.new(@project)
|
||||
kubectl.apply_yaml(pod.to_yaml)
|
||||
redirect_to project_processes_path(@project)
|
||||
end
|
||||
|
||||
def show
|
||||
client = K8::Client.new(@project.cluster.kubeconfig)
|
||||
@logs = client.get_pod_log(params[:id], @project.name)
|
||||
end
|
||||
|
||||
def destroy
|
||||
client = K8::Client.from_project(@project)
|
||||
client.delete_pod(params[:id], @project.name)
|
||||
redirect_to project_processes_path(@project), notice: "Pod #{params[:id]} deleted"
|
||||
end
|
||||
|
||||
private
|
||||
def get_pods_for_project(project)
|
||||
# Get all pods for a given namespace
|
||||
client = K8::Client.from_project(project).client
|
||||
pods = client.get_pods(namespace: project.name)
|
||||
end
|
||||
|
||||
def set_cluster
|
||||
@cluster = current_user.clusters.find(params[:cluster_id])
|
||||
end
|
||||
end
|
||||
@@ -79,7 +79,7 @@ class ProjectsController < ApplicationController
|
||||
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_project
|
||||
@project = Project.find(params[:id])
|
||||
@project = current_user.projects.find(params[:id])
|
||||
|
||||
# Uncomment to authorize with Pundit
|
||||
# authorize @project
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class StaticController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
end
|
||||
end
|
||||
|
||||
13
app/javascript/controllers/processes_controller.js
Normal file
13
app/javascript/controllers/processes_controller.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
connect() {
|
||||
console.log("processes controller connected")
|
||||
}
|
||||
|
||||
showConnectionInstructions(event) {
|
||||
event.preventDefault()
|
||||
click_outside_modal.showModal()
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ module K8
|
||||
class Client
|
||||
attr_reader :client
|
||||
|
||||
delegate :get_persistent_volume_claims, :get_services, :get_pods, :get_pod_log, to: :client
|
||||
delegate :get_persistent_volume_claims, :get_services, :get_pods, :get_pod_log, :delete_pod, to: :client
|
||||
|
||||
def self.from_project(project)
|
||||
new(project.cluster.kubeconfig)
|
||||
|
||||
8
app/services/k8/stateless/pod.rb
Normal file
8
app/services/k8/stateless/pod.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class K8::Stateless::Pod < K8::Base
|
||||
attr_accessor :project, :id
|
||||
|
||||
def initialize(project)
|
||||
@project = project
|
||||
@id = SecureRandom.uuid[0..7]
|
||||
end
|
||||
end
|
||||
@@ -14,9 +14,9 @@
|
||||
Environment
|
||||
</div>
|
||||
<% end %>
|
||||
<%= link_to project_logs_path(project), class: "tab hover:bg-base-content/15 #{'tab-active' if current_page?(project_logs_path(project))}" do %>
|
||||
<%= link_to project_processes_path(project), class: "tab hover:bg-base-content/15 #{'tab-active' if current_page?(project_processes_path(project))}" do %>
|
||||
<div class="flex items-center gap-2">
|
||||
Logs
|
||||
Processes
|
||||
</div>
|
||||
<% end %>
|
||||
<%= link_to project_metrics_url(project), class: "tab hover:bg-base-content/15 #{'tab-active' if current_page?(project_metrics_url(project))}" do %>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<%= project_layout(@project) do %>
|
||||
<%= turbo_frame_tag "pod_logs" do %>
|
||||
<% if @pods.empty? %>
|
||||
<div>
|
||||
<p class="text-gray-500">Nothing running for this project</p>
|
||||
</div>
|
||||
<% else %>
|
||||
<table class="table mt-2 rounded-box" data-component="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="text-sm font-medium text-base-content/80">Pod Name</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="text-sm font-medium text-base-content/80">
|
||||
Status
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @pods.each do |pod| %>
|
||||
<tr class="cursor-pointer hover:bg-base-200/40">
|
||||
<td>
|
||||
<div class="flex items-center space-x-3 truncate">
|
||||
<div class="font-medium">
|
||||
<%= pod.metadata.name %>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">
|
||||
<%= pod.status.phase %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">
|
||||
<%= link_to "Show Logs", project_log_path(@project, pod.metadata.name) %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
24
app/views/projects/processes/_connect_modal.html.erb
Normal file
24
app/views/projects/processes/_connect_modal.html.erb
Normal file
@@ -0,0 +1,24 @@
|
||||
<dialog aria-label="Modal" class="modal" id="click_outside_modal">
|
||||
<div class="modal-box w-11/12 max-w-5xl bg-base-300">
|
||||
<form method="dialog">
|
||||
<button aria-label="Close modal" class="btn btn-circle btn-ghost btn-sm absolute right-2 top-2">
|
||||
<iconify-icon icon="lucide:x" height="16"></iconify-icon>
|
||||
</button>
|
||||
</form>
|
||||
<div class="mb-8 w-full text-xl font-bold">Run this command to connect to your pod</div>
|
||||
<div class="my-4">
|
||||
<h4 class="text-lg font-bold">Step 1: Download Kubeconfig</h4>
|
||||
<%= link_to "Download Kubeconfig", download_kubeconfig_cluster_path(@project.cluster), class: "btn btn-sm btn-primary btn-outline", target: "_blank" %>
|
||||
</div>
|
||||
|
||||
<div class="my-4">
|
||||
<h4 class="text-lg font-bold">Step 2: Run the command</h4>
|
||||
<div role="tooltip" data-tip="Click to copy" class="tooltip tooltip-secondary">
|
||||
<code class="hover:cursor-pointer"><pre>KUBECONFIG=/path/to/kubeconfig kubectl exec -it {pod_name} -- /bin/bash</pre></code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
85
app/views/projects/processes/index.html.erb
Normal file
85
app/views/projects/processes/index.html.erb
Normal file
@@ -0,0 +1,85 @@
|
||||
<%= project_layout(@project) do %>
|
||||
<div class="mb-4" data-controller="processes">
|
||||
<h3 class="text-lg font-medium">Processes</h3>
|
||||
<%= button_to project_processes_path(@project), method: :post, class: "btn btn-sm btn-primary" do %>
|
||||
<iconify-icon icon="ic:baseline-plus"></iconify-icon>
|
||||
Create One-Off Pod
|
||||
<% end %>
|
||||
<div class="help-text mt-2">
|
||||
One-off pods are useful for running a single instance of a pod for a short period of time, so you can test something or run a command that doesn't need to be permanent.
|
||||
</div>
|
||||
</div>
|
||||
<%= turbo_frame_tag "pod_logs" do %>
|
||||
<% if @pods.empty? %>
|
||||
<div>
|
||||
<p class="text-gray-500">Nothing running for this project</p>
|
||||
</div>
|
||||
<% else %>
|
||||
<table class="table mt-2 rounded-box" data-component="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="text-sm font-medium text-base-content/80">Pod Name</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="text-sm font-medium text-base-content/80">
|
||||
Status
|
||||
</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="text-sm font-medium text-base-content/80">
|
||||
Created At
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @pods.each do |pod| %>
|
||||
<tr class="cursor-pointer hover:bg-base-200/40">
|
||||
<td>
|
||||
<div class="flex items-center space-x-3 truncate">
|
||||
<div class="font-medium">
|
||||
<%= pod.metadata.name %>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">
|
||||
<%= pod.status.phase %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="font-medium">
|
||||
<%= Time.parse(pod.metadata.creationTimestamp).to_formatted_s(:short) %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="font-medium">
|
||||
<%= link_to "Show Logs", project_process_path(@project, pod.metadata.name), class: "btn btn-sm btn-primary btn-outline" %>
|
||||
</div>
|
||||
|
||||
<div class="font-medium">
|
||||
<button class="btn btn-sm btn-info btn-outline" data-action="processes#showConnectionInstructions">Connect</button>
|
||||
</div>
|
||||
|
||||
<div class="font-medium">
|
||||
<% if pod.status.phase != "Running" || pod.metadata.labels.oneoff %>
|
||||
<%= link_to "Delete", project_process_path(@project, pod.metadata.name), method: :delete, class: "btn btn-sm btn-error btn-outline" %>
|
||||
<% else %>
|
||||
<div role="tooltip" data-tip="Be careful when deleting running pods, it can cause downtime for your project" class="tooltip tooltip-secondary">
|
||||
<%= link_to "Delete", project_process_path(@project, pod.metadata.name), method: :delete, class: "btn btn-sm btn-error btn-outline" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render "projects/processes/connect_modal" %>
|
||||
@@ -8,7 +8,6 @@ Rails.application.routes.draw do
|
||||
get "/privacy", to: "static#privacy"
|
||||
get "/terms", to: "static#terms"
|
||||
|
||||
|
||||
authenticated :user do
|
||||
root to: "projects#index", as: :user_root
|
||||
# Alternate route to use if logged in users should still see public root
|
||||
@@ -25,7 +24,7 @@ Rails.application.routes.draw do
|
||||
collection do
|
||||
get "/:project_id/deployments", to: "projects/deployments#index", as: :root
|
||||
end
|
||||
resources :logs, only: %i[index show], module: :projects
|
||||
resources :processes, only: %i[index show create destroy], module: :projects
|
||||
resources :services, only: %i[index new create destroy update], module: :projects do
|
||||
resources :domains, only: %i[create destroy], module: :services
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// `yarn build` - Build JavaScript and exit
|
||||
// `yarn build --watch` - Rebuild JavaScript on change
|
||||
// `yarn build --reload` - Reloads page when views, JavaScript, or stylesheets change
|
||||
// `yarn build --reload` - Reloads page when views, JavaScript, or stylesheets change. Requires a PORT to listen on. Defaults to 3200 but can be specified with PORT env var
|
||||
//
|
||||
// Minify is enabled when "RAILS_ENV=production"
|
||||
// Sourcemaps are enabled in non-production environments
|
||||
@@ -18,14 +18,13 @@ import { setTimeout } from "timers/promises"
|
||||
|
||||
const clients = []
|
||||
const entryPoints = [
|
||||
"application.js"
|
||||
"application.js",
|
||||
]
|
||||
const watchDirectories = [
|
||||
"./app/javascript/*.js",
|
||||
"./app/javascript/**/*.js",
|
||||
"./app/javascript/**/**/*.js",
|
||||
"./app/views/**/*.erb",
|
||||
"./app/views/**/*.html.erb",
|
||||
"./app/assets/builds/**/*.css", // Wait for cssbundling changes
|
||||
"./config/locales/**/*.yml",
|
||||
]
|
||||
const config = {
|
||||
absWorkingDir: path.join(process.cwd(), "app/javascript"),
|
||||
@@ -39,11 +38,18 @@ const config = {
|
||||
|
||||
async function buildAndReload() {
|
||||
// Foreman & Overmind assign a separate PORT for each process
|
||||
const port = parseInt(process.env.PORT || 6543)
|
||||
const port = parseInt(process.env.PORT || 3200)
|
||||
console.log(`Esbuild is listening on port ${port}`)
|
||||
const context = await esbuild.context({
|
||||
...config,
|
||||
banner: {
|
||||
js: ` (() => new EventSource("http://localhost:${port}").onmessage = () => location.reload())();`,
|
||||
js: `
|
||||
(() => {
|
||||
if (typeof EventSource !== 'undefined') {
|
||||
new EventSource("http://localhost:${port}").onmessage = () => location.reload()
|
||||
}
|
||||
})();
|
||||
`,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: <%= name %>-pod
|
||||
namespace: default
|
||||
name: <%= project.name %>-run-<%= id %>
|
||||
namespace: <%= project.name %>
|
||||
labels:
|
||||
oneoff: 'true'
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- image: <%= container %>
|
||||
- name: <%= project.name %>
|
||||
image: <%= project.container_registry_url %>
|
||||
command:
|
||||
- sleep
|
||||
- "3600"
|
||||
imagePullPolicy: Always
|
||||
name: <%= container %>
|
||||
restartPolicy: Always
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: <%= project.name %>
|
||||
imagePullSecrets:
|
||||
- name: dockerconfigjson-github-com
|
||||
|
||||
Reference in New Issue
Block a user