16 Commits

Author SHA1 Message Date
9bd8d0c131 all actions should use ruby 3.4
All checks were successful
Gitea Actions Demo / lint (push) Successful in 2m13s
Gitea Actions Demo / test (push) Successful in 2m17s
Gitea Actions Demo / docker (push) Successful in 1m31s
2025-03-30 17:52:00 -05:00
c2c1672802 add docker/metadata-action 2025-03-30 17:51:24 -05:00
af87ba5ed1 devcontainer: enable accessing host Docker socket
All checks were successful
Gitea Actions Demo / lint (push) Successful in 2m29s
Gitea Actions Demo / test (push) Successful in 2m20s
Gitea Actions Demo / release-image (push) Successful in 2m12s
2025-03-30 15:58:41 -05:00
af9f89a7f5 add hostname helper
All checks were successful
Gitea Actions Demo / lint (push) Successful in 29s
Gitea Actions Demo / test (push) Successful in 34s
Gitea Actions Demo / release-image (push) Has been skipped
2025-03-25 20:00:37 -05:00
6f84053124 devcontainer: remove /run directory creation 2025-03-25 19:58:51 -05:00
9c55277924 set the application name 2025-03-25 19:57:30 -05:00
2b7b338742 added /_cat/pid
All checks were successful
Gitea Actions Demo / lint (push) Successful in 26s
Gitea Actions Demo / test (push) Successful in 15s
Gitea Actions Demo / release-image (push) Has been skipped
2025-03-25 18:49:49 -05:00
312e570a02 add /_cat to list all of the other /_cat sub-endpoints 2025-03-25 18:48:25 -05:00
d6b1ed1046 remove unneccessary .gitignore patterns
All checks were successful
Gitea Actions Demo / lint (push) Successful in 21s
Gitea Actions Demo / test (push) Successful in 20s
Gitea Actions Demo / release-image (push) Has been skipped
2025-03-24 15:07:40 -05:00
768a58849d configure VS Code workspace to use Standard Ruby 2025-03-24 15:07:40 -05:00
54bc100d2a upgrade to bundler 2.6.6 2025-03-24 15:07:40 -05:00
c2b00f2c3e remove rubocop 2025-03-24 15:07:40 -05:00
a580e78d81 add devcontainer 2025-03-24 15:07:40 -05:00
8acc46c08d create a new method for inferring the PPID 2025-03-24 15:04:01 -05:00
8b094d64b1 add ?flakey=pct param to make the app return 500 errors intermittently
All checks were successful
Gitea Actions Demo / lint (push) Successful in 28s
Gitea Actions Demo / test (push) Successful in 16s
Gitea Actions Demo / release-image (push) Has been skipped
2025-03-14 19:14:56 -05:00
e1f29b555c store status files in /dev/shm
All checks were successful
Gitea Actions Demo / lint (push) Successful in 20s
Gitea Actions Demo / test (push) Successful in 15s
Gitea Actions Demo / release-image (push) Successful in 1m53s
2025-03-12 17:36:14 -05:00
14 changed files with 166 additions and 137 deletions

2
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,2 @@
ARG VARIANT="3.4.2"
FROM ghcr.io/rails/devcontainer/images/ruby:${VARIANT}

4
.devcontainer/boot.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
bundle update --bundler
bundler install

12
.devcontainer/compose.yml Normal file
View File

@ -0,0 +1,12 @@
---
services:
kubernaut:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspace:cached
- /var/run/docker.sock:/var/run/docker-host.sock
command: sleep infinity
memcached:
image: memcached:latest

View File

@ -0,0 +1,20 @@
{
"dockerComposeFile": "compose.yml",
"service": "kubernaut",
"workspaceFolder": "/workspace",
"customizations": {
"vscode": {
"extensions": [
"Shopify.ruby-lsp",
"ms-azuretools.vscode-docker"
]
}
},
"postCreateCommand": ".devcontainer/boot.sh",
"forwardPorts": [
4567
],
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
}
}

View File

@ -1,7 +1,16 @@
--- ---
name: Gitea Actions Demo name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀 run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
on:
schedule:
- cron: "0 10 * * *"
push:
branches:
- "**"
tags:
- "v*.*.*"
pull_request:
jobs: jobs:
lint: lint:
@ -16,7 +25,7 @@ jobs:
- name: Ruby Setup - name: Ruby Setup
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: '3.3' ruby-version: '3.4'
bundler-cache: true bundler-cache: true
- run: bundle install - run: bundle install
@ -40,9 +49,8 @@ jobs:
- run: bundle exec rake - run: bundle exec rake
release-image: docker:
needs: test needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: catthehacker/ubuntu:act-latest image: catthehacker/ubuntu:act-latest
@ -55,6 +63,21 @@ jobs:
with: with:
fetch-depth: 0 # all history for all branches and tags fetch-depth: 0 # all history for all branches and tags
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
git.kill0.net/ryanc/kubernaut
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
@ -68,5 +91,6 @@ jobs:
- name: Docker build and push - name: Docker build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
push: true push: ${{ github.event_name != 'pull_request' }}
tags: git.kill0.net/ryanc/kubernaut:latest tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

4
.gitignore vendored
View File

@ -1,5 +1,3 @@
.bundle .bundle
.cache
.local
.ruby-lsp .ruby-lsp
.ash_history /vendor

9
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"[ruby]": {
"editor.defaultFormatter": "Shopify.ruby-lsp"
},
"rubyLsp.formatter": "standard",
"rubyLsp.linters": [
"standard"
],
}

View File

@ -9,8 +9,6 @@ RUN <<EOT
apk upgrade --no-cache apk upgrade --no-cache
EOT EOT
RUN mkdir -p /run/app
FROM base AS build FROM base AS build
@ -25,25 +23,12 @@ RUN <<EOT
bundle install bundle install
EOT EOT
FROM build AS dev
WORKDIR /app
RUN <<EOT
bundle install
EOT
CMD [ "sleep", "infinity" ]
FROM base FROM base
# RUN useradd ruby --home /app --shell /bin/sh # RUN useradd ruby --home /app --shell /bin/sh
RUN adduser ruby -h /app -D RUN adduser ruby -h /app -D
RUN mkdir -p /run/app
RUN chown ruby:ruby /run/app
USER ruby:ruby USER ruby:ruby
COPY --from=build /usr/local/bundle /usr/local/bundle COPY --from=build /usr/local/bundle /usr/local/bundle

View File

@ -16,8 +16,6 @@ gem "httparty"
group :development do group :development do
gem "ruby-lsp" gem "ruby-lsp"
gem "rubocop"
gem "rbs"
gem "rack-test" gem "rack-test"
gem "rspec" gem "rspec"
gem "standard" gem "standard"

View File

@ -2,7 +2,7 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
anyflake (0.0.1) anyflake (0.0.1)
ast (2.4.2) ast (2.4.3)
base64 (0.2.0) base64 (0.2.0)
bigdecimal (3.1.8) bigdecimal (3.1.8)
csv (3.3.0) csv (3.3.0)
@ -11,7 +11,7 @@ GEM
csv csv
mini_mime (>= 1.0.0) mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
json (2.10.1) json (2.10.2)
jwt (2.10.1) jwt (2.10.1)
base64 base64
ksuid (1.0.0) ksuid (1.0.0)
@ -28,7 +28,7 @@ GEM
nanoid (2.0.0) nanoid (2.0.0)
nio4r (2.7.4) nio4r (2.7.4)
parallel (1.26.3) parallel (1.26.3)
parser (3.3.7.1) parser (3.3.7.2)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
prism (1.3.0) prism (1.3.0)
@ -65,9 +65,10 @@ GEM
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-support (3.13.2) rspec-support (3.13.2)
rubocop (1.71.2) rubocop (1.73.2)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.3.0.2) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
@ -75,11 +76,12 @@ GEM
rubocop-ast (>= 1.38.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.38.1) rubocop-ast (1.41.0)
parser (>= 3.3.1.0) parser (>= 3.3.7.2)
rubocop-performance (1.23.1) rubocop-performance (1.24.0)
rubocop (>= 1.48.1, < 2.0) lint_roller (~> 1.1)
rubocop-ast (>= 1.31.1, < 2.0) rubocop (>= 1.72.1, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
ruby-lsp (0.23.11) ruby-lsp (0.23.11)
language_server-protocol (~> 3.17.0) language_server-protocol (~> 3.17.0)
prism (>= 1.2, < 2.0) prism (>= 1.2, < 2.0)
@ -101,18 +103,18 @@ GEM
sinatra (= 4.1.1) sinatra (= 4.1.1)
tilt (~> 2.0) tilt (~> 2.0)
sorbet-runtime (0.5.11911) sorbet-runtime (0.5.11911)
standard (1.45.0) standard (1.47.0)
language_server-protocol (~> 3.17.0.2) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0) lint_roller (~> 1.0)
rubocop (~> 1.71.0) rubocop (~> 1.73.0)
standard-custom (~> 1.0.0) standard-custom (~> 1.0.0)
standard-performance (~> 1.6) standard-performance (~> 1.7)
standard-custom (1.0.2) standard-custom (1.0.2)
lint_roller (~> 1.0) lint_roller (~> 1.0)
rubocop (~> 1.50) rubocop (~> 1.50)
standard-performance (1.6.0) standard-performance (1.7.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop-performance (~> 1.23.0) rubocop-performance (~> 1.24.0)
tilt (2.6.0) tilt (2.6.0)
ulid (1.4.0) ulid (1.4.0)
unicode-display_width (3.1.4) unicode-display_width (3.1.4)
@ -137,9 +139,7 @@ DEPENDENCIES
rack-test rack-test
rackup rackup
rake rake
rbs
rspec rspec
rubocop
ruby-lsp ruby-lsp
sinatra sinatra
sinatra-contrib sinatra-contrib
@ -148,4 +148,4 @@ DEPENDENCIES
uuid7 uuid7
BUNDLED WITH BUNDLED WITH
2.5.13 2.6.6

82
app.rb
View File

@ -23,11 +23,12 @@ require "config"
CHUNK_SIZE = 1024**2 CHUNK_SIZE = 1024**2
SESSION_SECRET_HEX_LENGTH = 64 SESSION_SECRET_HEX_LENGTH = 64
JWT_SECRET_HEX_LENGTH = 64 JWT_SECRET_HEX_LENGTH = 64
DEFAULT_FLAKEY = 50
ENV_PREFIX = "KUBERNAUT" NAME = "kubernaut".freeze
ENV_PREFIX = NAME.upcase
CLK_TCK = 100 CLK_TCK = 100
PID_FILE_PATH = "/run/app/pid".freeze
PROC_UPTIME_PATH = "/proc/uptime".freeze PROC_UPTIME_PATH = "/proc/uptime".freeze
SECONDS_PER_YEAR = 31_556_952 SECONDS_PER_YEAR = 31_556_952
@ -108,7 +109,7 @@ end
class TickTock class TickTock
def initialize def initialize
@pid = master_pid @pid = ppid
@procfs_f = format "/proc/%s/stat", @pid @procfs_f = format "/proc/%s/stat", @pid
puts @pid puts @pid
end end
@ -134,7 +135,7 @@ class Health
include UpDown include UpDown
def initialize def initialize
@file = "./healthy" @file = "/dev/shm/healthy"
end end
def healthy? def healthy?
@ -148,7 +149,7 @@ class Ready
include UpDown include UpDown
def initialize def initialize
@file = "./ready" @file = "/dev/shm/ready"
end end
def ready? def ready?
@ -161,7 +162,7 @@ class Sleep
include State include State
def initialize def initialize
@file = "./sleep" @file = "/dev/shm/sleep"
end end
def asleep? def asleep?
@ -177,9 +178,22 @@ class Sleep
end end
end end
def master_pid def ppid
pid_s = File.read PID_FILE_PATH pid = Process.pid
Integer pid_s.strip # self
ps = File.open "/proc/#{pid}/stat", &:readline
ps = ps.split(" ")
ppid = Integer(ps[3])
# ppid
ps = File.open "/proc/#{ppid}/stat", &:readline
ps = ps.split(" ")
if ps[1].include? "ruby"
ppid
else
pid
end
end end
def system_uptime def system_uptime
@ -207,6 +221,13 @@ Health.instance.up
Ready.instance.up Ready.instance.up
Sleep.instance.wake Sleep.instance.wake
def flaky(pct = DEFAULT_FLAKEY)
r = Random.rand(0..100)
unless r < (100 - pct)
halt 500, "so unreliable"
end
end
enable :sessions enable :sessions
configure do configure do
@ -218,6 +239,17 @@ before do
sleep(1) while Sleep.instance.asleep? && request.path_info != "/livez/sleep" sleep(1) while Sleep.instance.asleep? && request.path_info != "/livez/sleep"
content_type :text if request.path_info.start_with? "/_cat" content_type :text if request.path_info.start_with? "/_cat"
request.session_options[:skip] = !request.path_info.start_with?("/session") request.session_options[:skip] = !request.path_info.start_with?("/session")
if params.has_key? :flaky
begin
pct = Integer(params[:flaky])
pct = pct.clamp(0, 100)
rescue => e
logger.warn "#{e.message}: falling back to default flaky percentage of #{DEFAULT_FLAKEY}"
pct = DEFAULT_FLAKEY
end
flaky(pct)
end
end end
helpers do helpers do
@ -248,6 +280,10 @@ helpers do
@auth.credentials and @auth.credentials and
@auth.credentials == ["qwer", "asdf"] @auth.credentials == ["qwer", "asdf"]
end end
def hostname
ENV["HOSTNAME"]
end
end end
get "/" do get "/" do
@ -331,19 +367,19 @@ get "/snowflake" do
end end
post "/quit" do post "/quit" do
Process.kill("TERM", master_pid) Process.kill("TERM", ppid)
nil nil
end end
post "/halt" do post "/halt" do
Process.kill("QUIT", master_pid) Process.kill("QUIT", ppid)
nil nil
end end
get "/pid" do get "/pid" do
pretty = params.key? :pretty pretty = params.key? :pretty
jsonify({puma: master_pid, pid: Process.pid}, pretty:) jsonify({ppid: ppid, pid: Process.pid}, pretty:)
end end
get "/token" do get "/token" do
@ -386,6 +422,20 @@ get "/config", provides: "json" do
jsonify config.as_json, pretty: jsonify config.as_json, pretty:
end end
get "/_cat" do
stream do |out|
out << "=^.^=\n"
x = Sinatra::Application.routes.map do |method, route|
route.map do |route|
route.first.to_s
end
end
x.flatten.sort.uniq.each do |route|
out << "#{route}\n" if route.start_with? "/_cat"
end
end
end
get "/_cat/headers" do get "/_cat/headers" do
stream do |out| stream do |out|
req_headers.each do |k, v| req_headers.each do |k, v|
@ -425,6 +475,14 @@ get "/_cat/config" do
end end
end end
get "/_cat/pid" do
stream do |out|
{ppid: ppid, pid: Process.pid}.sort.each do |k, v|
out << "#{k}=#{v}\n"
end
end
end
route :delete, :get, :patch, :post, :put, "/status/:code" do route :delete, :get, :patch, :post, :put, "/status/:code" do
# hello # hello
code = Integer(params[:code]) code = Integer(params[:code])

View File

@ -1,3 +0,0 @@
# workers 3
pidfile "/run/app/pid"
preload_app!

View File

@ -1,15 +0,0 @@
services:
web:
build:
context: .
target: dev
ports:
- "4567:4567"
volumes:
- .:/app
environment:
{}
# WEB_CONCURRENCY: 3
command:
- sleep
- infinity

View File

@ -1,63 +0,0 @@
module State
def enable: () -> untyped
def disable: () -> (untyped | nil)
def enabled?: () -> bool
def toggle: () -> untyped
end
module UpDown
def up: () -> untyped
def down: () -> untyped
def to_s: () -> ("up" | "down")
def to_json: (*untyped _args) -> (nil | untyped)
end
class Health
@file: untyped
include Singleton
include State
include UpDown
def initialize: () -> void
def healthy?: () -> bool
end
class Ready
@file: untyped
include Singleton
include State
include UpDown
def initialize: () -> void
def ready?: () -> bool
end
class Sleep
@file: untyped
include Singleton
include State
def initialize: () -> void
def asleep?: () -> bool
def wake: () -> untyped
def sleep: () -> untyped
end