chore: add current provisioning state before migration

This commit is contained in:
Jesús Pérez 2025-09-22 23:11:41 +01:00
parent a9703b4748
commit 50745b0f22
660 changed files with 88126 additions and 0 deletions

View file

@ -0,0 +1,135 @@
# Extension Loader
# Discovers and loads extensions from multiple sources
# Extension discovery paths in priority order
export def get-extension-paths []: nothing -> list<string> {
[
# Project-specific extensions (highest priority)
($env.PWD | path join ".provisioning" "extensions")
# User extensions
($env.HOME | path join ".provisioning-extensions")
# System-wide extensions
"/opt/provisioning-extensions"
# Environment variable override
($env.PROVISIONING_EXTENSIONS_PATH? | default "")
] | where ($it | is-not-empty) | where ($it | path exists)
}
# Load extension manifest
export def load-manifest [extension_path: string]: nothing -> record {
let manifest_file = ($extension_path | path join "manifest.yaml")
if ($manifest_file | path exists) {
open $manifest_file
} else {
{
name: ($extension_path | path basename)
version: "1.0.0"
type: "unknown"
requires: []
permissions: []
hooks: {}
}
}
}
# Check if extension is allowed
export def is-extension-allowed [manifest: record]: nothing -> bool {
let mode = ($env.PROVISIONING_EXTENSION_MODE? | default "full")
let allowed = ($env.PROVISIONING_ALLOWED_EXTENSIONS? | default "" | split row "," | each { str trim })
let blocked = ($env.PROVISIONING_BLOCKED_EXTENSIONS? | default "" | split row "," | each { str trim })
match $mode {
"disabled" => false,
"restricted" => {
if ($blocked | any {|x| $x == $manifest.name}) {
false
} else if ($allowed | is-empty) {
true
} else {
($allowed | any {|x| $x == $manifest.name})
}
},
_ => {
not ($blocked | any {|x| $x == $manifest.name})
}
}
}
# Discover providers in extension paths
export def discover-providers []: nothing -> table {
get-extension-paths | each {|ext_path|
let providers_path = ($ext_path | path join "providers")
if ($providers_path | path exists) {
glob ($providers_path | path join "*")
| where ($it | path type) == "dir"
| each {|provider_path|
let manifest = (load-manifest $provider_path)
if (is-extension-allowed $manifest) and $manifest.type == "provider" {
{
name: ($provider_path | path basename)
path: $provider_path
manifest: $manifest
source: $ext_path
}
} else {
null
}
}
| where ($it != null)
} else {
[]
}
} | flatten
}
# Discover taskservs in extension paths
export def discover-taskservs []: nothing -> table {
get-extension-paths | each {|ext_path|
let taskservs_path = ($ext_path | path join "taskservs")
if ($taskservs_path | path exists) {
glob ($taskservs_path | path join "*")
| where ($it | path type) == "dir"
| each {|taskserv_path|
let manifest = (load-manifest $taskserv_path)
if (is-extension-allowed $manifest) and $manifest.type == "taskserv" {
{
name: ($taskserv_path | path basename)
path: $taskserv_path
manifest: $manifest
source: $ext_path
}
} else {
null
}
}
| where ($it != null)
} else {
[]
}
} | flatten
}
# Check extension requirements
export def check-requirements [manifest: record]: nothing -> bool {
if ($manifest.requires | is-empty) {
true
} else {
$manifest.requires | all {|req|
(which $req | length) > 0
}
}
}
# Load extension hooks
export def load-hooks [extension_path: string, manifest: record]: nothing -> record {
if ($manifest.hooks | is-not-empty) {
$manifest.hooks | items {|key, value|
let hook_file = ($extension_path | path join $value)
if ($hook_file | path exists) {
{key: $key, value: $hook_file}
}
} | reduce --fold {} {|it, acc| $acc | insert $it.key $it.value}
} else {
{}
}
}

View file

@ -0,0 +1,6 @@
# Extensions Module
# Provides extension system functionality
export use loader.nu *
export use registry.nu *
export use profiles.nu *

View file

@ -0,0 +1,223 @@
# Profile-based Access Control
# Implements permission system for restricted environments like CI/CD
# Load profile configuration
export def load-profile [profile_name?: string]: nothing -> record {
let active_profile = if ($profile_name | is-not-empty) {
$profile_name
} else {
$env.PROVISIONING_PROFILE? | default ""
}
if ($active_profile | is-empty) {
return {
name: "default"
allowed: {
commands: []
providers: []
taskservs: []
}
blocked: {
commands: []
providers: []
taskservs: []
}
restricted: false
}
}
# Check user profile first
let user_profile_path = ($env.HOME | path join ".provisioning-extensions" "profiles" $"($active_profile).yaml")
let system_profile_path = ("/opt/provisioning-extensions/profiles" | path join $"($active_profile).yaml")
let project_profile_path = ($env.PWD | path join ".provisioning" "profiles" $"($active_profile).yaml")
# Load in priority order: project > user > system
let available_files = [
$project_profile_path
$user_profile_path
$system_profile_path
] | where ($it | path exists)
if ($available_files | length) > 0 {
open ($available_files | first)
} else {
# Default restricted profile
{
name: $active_profile
allowed: {
commands: ["list", "status", "show", "query", "help", "version"]
providers: ["local"]
taskservs: []
}
blocked: {
commands: ["delete", "create", "sops", "secrets"]
providers: ["aws", "upcloud"]
taskservs: []
}
restricted: true
}
}
}
# Check if command is allowed
export def is-command-allowed [command: string, subcommand?: string]: nothing -> bool {
let profile = (load-profile)
if not $profile.restricted {
return true
}
let full_command = if ($subcommand | is-not-empty) {
$"($command) ($subcommand)"
} else {
$command
}
# Check blocked first
if ($profile.blocked.commands | any {|cmd| $full_command =~ $cmd}) {
return false
}
# If allowed list is empty, allow everything not blocked
if ($profile.allowed.commands | is-empty) {
return true
}
# Check if explicitly allowed
($profile.allowed.commands | any {|cmd| $full_command =~ $cmd})
}
# Check if provider is allowed
export def is-provider-allowed [provider: string]: nothing -> bool {
let profile = (load-profile)
if not $profile.restricted {
return true
}
# Check blocked first
if ($profile.blocked.providers | any {|prov| $provider == $prov}) {
return false
}
# If allowed list is empty, allow everything not blocked
if ($profile.allowed.providers | is-empty) {
return true
}
# Check if explicitly allowed
($profile.allowed.providers | any {|prov| $provider == $prov})
}
# Check if taskserv is allowed
export def is-taskserv-allowed [taskserv: string]: nothing -> bool {
let profile = (load-profile)
if not $profile.restricted {
return true
}
# Check blocked first
if ($profile.blocked.taskservs | any {|ts| $taskserv == $ts}) {
return false
}
# If allowed list is empty, allow everything not blocked
if ($profile.allowed.taskservs | is-empty) {
return true
}
# Check if explicitly allowed
($profile.allowed.taskservs | any {|ts| $taskserv == $ts})
}
# Enforce profile restrictions on command execution
export def enforce-profile [command: string, subcommand?: string, target?: string]: nothing -> bool {
if not (is-command-allowed $command $subcommand) {
print $"🛑 Command '($command) ($subcommand | default "")' is not allowed by profile ($env.PROVISIONING_PROFILE)"
return false
}
# Additional checks based on target type
if ($target | is-not-empty) {
match $command {
"server" => {
if ($subcommand | default "") in ["create", "delete"] {
let settings = (find_get_settings)
let server = ($settings.data.servers | where hostname == $target | first?)
if ($server | is-not-empty) {
if not (is-provider-allowed $server.provider) {
print $"🛑 Provider '($server.provider)' is not allowed by profile"
return false
}
}
}
}
"taskserv" => {
if not (is-taskserv-allowed $target) {
print $"🛑 TaskServ '($target)' is not allowed by profile"
return false
}
}
}
}
return true
}
# Show current profile information
export def show-profile []: nothing -> record {
let profile = (load-profile)
{
active_profile: ($env.PROVISIONING_PROFILE? | default "default")
extension_mode: ($env.PROVISIONING_EXTENSION_MODE? | default "full")
profile_config: $profile
status: (if $profile.restricted { "restricted" } else { "unrestricted" })
}
}
# Create example profile files
export def create-example-profiles []: nothing -> nothing {
let user_profiles_dir = ($env.HOME | path join ".provisioning-extensions" "profiles")
mkdir $user_profiles_dir
# CI/CD profile
let cicd_profile = {
profile: "cicd"
description: "Restricted profile for CI/CD agents"
restricted: true
allowed: {
commands: ["server list", "server status", "taskserv list", "taskserv status", "query", "show", "help", "version"]
providers: ["local"]
taskservs: ["kubernetes", "containerd", "kubectl"]
}
blocked: {
commands: ["server create", "server delete", "taskserv create", "taskserv delete", "sops", "secrets"]
providers: ["aws", "upcloud"]
taskservs: ["postgres", "gitea"]
}
}
# Developer profile
let developer_profile = {
profile: "developer"
description: "Profile for developers with limited production access"
restricted: true
allowed: {
commands: ["server list", "server create", "taskserv list", "taskserv create", "query", "show"]
providers: ["local", "aws"]
taskservs: []
}
blocked: {
commands: ["server delete", "sops"]
providers: ["upcloud"]
taskservs: ["postgres"]
}
}
# Save example profiles
$cicd_profile | to yaml | save ($user_profiles_dir | path join "cicd.yaml")
$developer_profile | to yaml | save ($user_profiles_dir | path join "developer.yaml")
print $"Created example profiles in ($user_profiles_dir)"
}

View file

@ -0,0 +1,237 @@
# Extension Registry
# Manages registration and lookup of providers, taskservs, and hooks
use loader.nu *
# Get default extension registry
export def get-default-registry []: nothing -> record {
{
providers: {},
taskservs: {},
hooks: {
pre_server_create: [],
post_server_create: [],
pre_server_delete: [],
post_server_delete: [],
pre_taskserv_install: [],
post_taskserv_install: [],
pre_taskserv_delete: [],
post_taskserv_delete: []
}
}
}
# Get registry cache file path
def get-registry-cache-file []: nothing -> string {
let cache_dir = ($env.HOME | path join ".cache" "provisioning")
if not ($cache_dir | path exists) {
mkdir $cache_dir
}
$cache_dir | path join "extension-registry.json"
}
# Load registry from cache or initialize
export def load-registry []: nothing -> record {
let cache_file = (get-registry-cache-file)
if ($cache_file | path exists) {
open $cache_file
} else {
get-default-registry
}
}
# Save registry to cache
export def save-registry [registry: record]: nothing -> nothing {
let cache_file = (get-registry-cache-file)
$registry | to json | save -f $cache_file
}
# Initialize extension registry
export def init-registry []: nothing -> nothing {
# Load all discovered extensions
let providers = (discover-providers)
let taskservs = (discover-taskservs)
# Build provider entries
let provider_entries = ($providers | reduce -f {} {|provider, acc|
let provider_entry = {
name: $provider.name
path: $provider.path
manifest: $provider.manifest
entry_point: ($provider.path | path join "nulib" $provider.name)
available: ($provider.path | path join "nulib" $provider.name | path exists)
}
if $provider_entry.available {
$acc | insert $provider.name $provider_entry
} else {
$acc
}
})
# Build taskserv entries
let taskserv_entries = ($taskservs | reduce -f {} {|taskserv, acc|
let taskserv_entry = {
name: $taskserv.name
path: $taskserv.path
manifest: $taskserv.manifest
profiles: (glob ($taskserv.path | path join "*") | where ($it | path type) == "dir" | each { path basename })
available: true
}
$acc | insert $taskserv.name $taskserv_entry
})
# Build hooks (simplified for now)
let hook_entries = (get-default-registry).hooks
# Build final registry
let registry = {
providers: $provider_entries
taskservs: $taskserv_entries
hooks: $hook_entries
}
# Save registry to cache
save-registry $registry
}
# Register a provider
export def --env register-provider [name: string, path: string, manifest: record]: nothing -> nothing {
let provider_entry = {
name: $name
path: $path
manifest: $manifest
entry_point: ($path | path join "nulib" $name)
available: ($path | path join "nulib" $name | path exists)
}
if $provider_entry.available {
let current_registry = ($env.EXTENSION_REGISTRY? | default (get-default-registry))
$env.EXTENSION_REGISTRY = ($current_registry
| update providers ($current_registry.providers | insert $name $provider_entry))
}
}
# Register a taskserv
export def --env register-taskserv [name: string, path: string, manifest: record]: nothing -> nothing {
let taskserv_entry = {
name: $name
path: $path
manifest: $manifest
profiles: (glob ($path | path join "*") | where ($it | path type) == "dir" | each { path basename })
available: true
}
let current_registry = ($env.EXTENSION_REGISTRY? | default (get-default-registry))
$env.EXTENSION_REGISTRY = ($current_registry
| update taskservs ($current_registry.taskservs | insert $name $taskserv_entry))
}
# Register a hook
export def --env register-hook [hook_type: string, hook_path: string, extension_name: string]: nothing -> nothing {
let hook_entry = {
path: $hook_path
extension: $extension_name
enabled: true
}
let current_registry = ($env.EXTENSION_REGISTRY? | default (get-default-registry))
let current_hooks = ($current_registry.hooks? | get -o $hook_type | default [])
$env.EXTENSION_REGISTRY = ($current_registry
| update hooks ($current_registry.hooks? | default (get-default-registry).hooks
| update $hook_type ($current_hooks | append $hook_entry)))
}
# Get registered provider
export def get-provider [name: string]: nothing -> record {
let registry = (load-registry)
$registry.providers | get -o $name | default {}
}
# List all registered providers
export def list-providers []: nothing -> table {
let registry = (load-registry)
$registry.providers | items {|name, provider|
{
name: $name
path: $provider.path
version: $provider.manifest.version
available: $provider.available
source: ($provider.path | str replace $env.HOME "~")
}
} | flatten
}
# Get registered taskserv
export def get-taskserv [name: string]: nothing -> record {
let registry = (load-registry)
$registry.taskservs | get -o $name | default {}
}
# List all registered taskservs
export def list-taskservs []: nothing -> table {
let registry = (load-registry)
$registry.taskservs | items {|name, taskserv|
{
name: $name
path: $taskserv.path
version: $taskserv.manifest.version
profiles: ($taskserv.profiles | str join ", ")
source: ($taskserv.path | str replace $env.HOME "~")
}
} | flatten
}
# Execute hooks
export def execute-hooks [hook_type: string, context: record]: nothing -> list {
let registry = (load-registry)
let hooks = ($registry.hooks? | get -o $hook_type | default [])
$hooks | where enabled | each {|hook|
let result = (do { nu $hook.path ($context | to json) } | complete)
if $result.exit_code == 0 {
{
hook: $hook.path
extension: $hook.extension
output: $result.stdout
success: true
}
} else {
{
hook: $hook.path
extension: $hook.extension
error: $result.stderr
success: false
}
}
}
}
# Check if provider exists (core or extension)
export def provider-exists [name: string]: nothing -> bool {
let core_providers = ["aws", "local", "upcloud"]
($name in $core_providers) or ((get-provider $name) | is-not-empty)
}
# Check if taskserv exists (core or extension)
export def taskserv-exists [name: string]: nothing -> bool {
let core_path = ($env.PROVISIONING_TASKSERVS_PATH | path join $name)
let extension_taskserv = (get-taskserv $name)
($core_path | path exists) or ($extension_taskserv | is-not-empty)
}
# Get taskserv path (core or extension)
export def get-taskserv-path [name: string]: nothing -> string {
let core_path = ($env.PROVISIONING_TASKSERVS_PATH | path join $name)
if ($core_path | path exists) {
$core_path
} else {
let extension_taskserv = (get-taskserv $name)
if ($extension_taskserv | is-not-empty) {
$extension_taskserv.path
} else {
""
}
}
}