feat: Implement Kubernetes installer for kubectl
- Add install functionality for kubectl - Implement destroy functionality for kubectl - Add platform-specific download URLs for kubectl - Ensure .kube directory is created with correct permissions
This commit is contained in:
12
lib/installers/virt/kubernetes_installer/.heroscript
Normal file
12
lib/installers/virt/kubernetes_installer/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'KubernetesInstaller'
|
||||
singleton:1
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:0
|
||||
hasconfig:1
|
||||
build:0
|
||||
@@ -0,0 +1,120 @@
|
||||
module kubernetes_installer
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.texttools
|
||||
import incubaid.herolib.core
|
||||
import incubaid.herolib.installers.ulist
|
||||
import os
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if kubectl is installed and meets minimum version requirement
|
||||
fn installed() !bool {
|
||||
if !osal.cmd_exists('kubectl') {
|
||||
return false
|
||||
}
|
||||
|
||||
res := os.execute('${osal.profile_path_source_and()!} kubectl version --client --output=json')
|
||||
if res.exit_code != 0 {
|
||||
// Try older kubectl version command format
|
||||
res2 := os.execute('${osal.profile_path_source_and()!} kubectl version --client --short')
|
||||
if res2.exit_code != 0 {
|
||||
return false
|
||||
}
|
||||
// Parse version from output like "Client Version: v1.31.0"
|
||||
lines := res2.output.split_into_lines().filter(it.contains('Client Version'))
|
||||
if lines.len == 0 {
|
||||
return false
|
||||
}
|
||||
version_str := lines[0].all_after('v').trim_space()
|
||||
if texttools.version(version) <= texttools.version(version_str) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// For newer kubectl versions with JSON output
|
||||
// Just check if kubectl exists and runs - version checking is optional
|
||||
return true
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
// Not applicable for kubectl
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install kubectl')
|
||||
|
||||
mut url := ''
|
||||
mut dest_path := '/tmp/kubectl'
|
||||
|
||||
// Determine download URL based on platform
|
||||
if core.is_linux_arm()! {
|
||||
url = 'https://dl.k8s.io/release/v${version}/bin/linux/arm64/kubectl'
|
||||
} else if core.is_linux_intel()! {
|
||||
url = 'https://dl.k8s.io/release/v${version}/bin/linux/amd64/kubectl'
|
||||
} else if core.is_osx_arm()! {
|
||||
url = 'https://dl.k8s.io/release/v${version}/bin/darwin/arm64/kubectl'
|
||||
} else if core.is_osx_intel()! {
|
||||
url = 'https://dl.k8s.io/release/v${version}/bin/darwin/amd64/kubectl'
|
||||
} else {
|
||||
return error('unsupported platform for kubectl installation')
|
||||
}
|
||||
|
||||
console.print_header('downloading kubectl from ${url}')
|
||||
|
||||
// Download kubectl binary
|
||||
osal.download(
|
||||
url: url
|
||||
// minsize_kb: 40000 // kubectl is ~45MB
|
||||
dest: dest_path
|
||||
)!
|
||||
|
||||
// Make it executable
|
||||
os.chmod(dest_path, 0o755)!
|
||||
|
||||
// Install to system
|
||||
osal.cmd_add(
|
||||
cmdname: 'kubectl'
|
||||
source: dest_path
|
||||
)!
|
||||
|
||||
// Create .kube directory with proper permissions
|
||||
kube_dir := os.join_path(os.home_dir(), '.kube')
|
||||
if !os.exists(kube_dir) {
|
||||
console.print_header('creating ${kube_dir} directory')
|
||||
os.mkdir_all(kube_dir)!
|
||||
os.chmod(kube_dir, 0o700)! // read/write/execute for owner only
|
||||
console.print_header('${kube_dir} directory created with permissions 0700')
|
||||
} else {
|
||||
// Ensure correct permissions even if directory exists
|
||||
os.chmod(kube_dir, 0o700)!
|
||||
console.print_header('${kube_dir} directory permissions set to 0700')
|
||||
}
|
||||
|
||||
console.print_header('kubectl installed successfully')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
console.print_header('destroy kubectl')
|
||||
|
||||
if !installed()! {
|
||||
console.print_header('kubectl is not installed')
|
||||
return
|
||||
}
|
||||
|
||||
// Remove kubectl command
|
||||
osal.cmd_delete('kubectl')!
|
||||
|
||||
// Clean up any temporary files
|
||||
osal.rm('/tmp/kubectl')!
|
||||
|
||||
console.print_header('kubectl destruction completed')
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
module kubernetes_installer
|
||||
|
||||
import incubaid.herolib.core.base
|
||||
import incubaid.herolib.core.playbook { PlayBook }
|
||||
import incubaid.herolib.ui.console
|
||||
import json
|
||||
|
||||
__global (
|
||||
kubernetes_installer_global map[string]&KubernetesInstaller
|
||||
kubernetes_installer_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&KubernetesInstaller {
|
||||
mut obj := KubernetesInstaller{
|
||||
name: args.name
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&KubernetesInstaller {
|
||||
mut context := base.context()!
|
||||
kubernetes_installer_default = args.name
|
||||
if args.fromdb || args.name !in kubernetes_installer_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:kubernetes_installer', args.name)! {
|
||||
data := r.hget('context:kubernetes_installer', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('KubernetesInstaller with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(KubernetesInstaller, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("KubernetesInstaller with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return kubernetes_installer_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for kubernetes_installer with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o KubernetesInstaller) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
kubernetes_installer_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:kubernetes_installer', o2.name, json.encode(o2))!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
return r.hexists('context:kubernetes_installer', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:kubernetes_installer', args.name)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ArgsList {
|
||||
pub mut:
|
||||
fromdb bool // will load from filesystem
|
||||
}
|
||||
|
||||
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
|
||||
pub fn list(args ArgsList) ![]&KubernetesInstaller {
|
||||
mut res := []&KubernetesInstaller{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
kubernetes_installer_global = map[string]&KubernetesInstaller{}
|
||||
kubernetes_installer_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:kubernetes_installer')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in kubernetes_installer_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o KubernetesInstaller) !KubernetesInstaller {
|
||||
mut o2 := obj_init(o)!
|
||||
kubernetes_installer_global[o2.name] = &o2
|
||||
kubernetes_installer_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'kubernetes_installer.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'kubernetes_installer.configure')!
|
||||
if install_actions.len > 0 {
|
||||
return error("can't configure kubernetes_installer, because no configuration allowed for this installer.")
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'kubernetes_installer.')!
|
||||
for mut other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action kubernetes_installer.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action kubernetes_installer.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
other_action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self KubernetesInstaller) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self KubernetesInstaller) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self KubernetesInstaller) destroy() ! {
|
||||
switch(self.name)
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for kubernetes_installer
|
||||
pub fn switch(name string) {
|
||||
kubernetes_installer_default = name
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
module kubernetes_installer
|
||||
|
||||
import incubaid.herolib.data.encoderhero
|
||||
|
||||
pub const version = '1.31.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// Kubernetes installer - handles kubectl installation
|
||||
@[heap]
|
||||
pub struct KubernetesInstaller {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ KubernetesInstaller) !KubernetesInstaller {
|
||||
mut mycfg := mycfg_
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
// No configuration needed for kubectl
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj KubernetesInstaller) !string {
|
||||
return encoderhero.encode[KubernetesInstaller](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !KubernetesInstaller {
|
||||
mut obj := encoderhero.decode[KubernetesInstaller](heroscript)!
|
||||
return obj
|
||||
}
|
||||
44
lib/installers/virt/kubernetes_installer/readme.md
Normal file
44
lib/installers/virt/kubernetes_installer/readme.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# kubernetes_installer
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```v
|
||||
|
||||
|
||||
import incubaid.herolib.installers.something.kubernetes_installer as kubernetes_installer_installer
|
||||
|
||||
heroscript:="
|
||||
!!kubernetes_installer.configure name:'test'
|
||||
password: '1234'
|
||||
port: 7701
|
||||
|
||||
!!kubernetes_installer.start name:'test' reset:1
|
||||
"
|
||||
|
||||
kubernetes_installer_installer.play(heroscript=heroscript)!
|
||||
|
||||
//or we can call the default and do a start with reset
|
||||
//mut installer:= kubernetes_installer_installer.get()!
|
||||
//installer.start(reset:true)!
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
|
||||
```hero
|
||||
!!kubernetes_installer.configure
|
||||
homedir: '/home/user/kubernetes_installer'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
title: 'Some Title'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user