change panic to fatal log
This commit is contained in:
@@ -57,7 +57,7 @@ func createConfig(configFile string, out string, password string, verbose bool)
|
|||||||
|
|
||||||
if err := cleanenv.ReadConfig(configFile, instance); err != nil {
|
if err := cleanenv.ReadConfig(configFile, instance); err != nil {
|
||||||
helper, _ := cleanenv.GetDescription(instance, nil)
|
helper, _ := cleanenv.GetDescription(instance, nil)
|
||||||
panic(fmt.Sprintf("%s\n%s", helper, err))
|
logging.Log.Fatal(fmt.Sprintf("%s\n%s", helper, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.BaseDir = files.CreateTempDir()
|
instance.BaseDir = files.CreateTempDir()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func InstallChart(chartSpec go_helm_client.ChartSpec) {
|
|||||||
logging.Log.Infof("Upgrading %s", chartSpec.ChartName)
|
logging.Log.Infof("Upgrading %s", chartSpec.ChartName)
|
||||||
}
|
}
|
||||||
if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec, nil); err != nil {
|
if _, err := helmClient.InstallOrUpgradeChart(context.Background(), &chartSpec, nil); err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package helm_client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"kube-forge/internal/config"
|
"kube-forge/internal/config"
|
||||||
|
"kube-forge/internal/logging"
|
||||||
|
|
||||||
"helm.sh/helm/v3/pkg/repo"
|
"helm.sh/helm/v3/pkg/repo"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,7 @@ func AddHelmRepo(namespace string, repoSettings config.RepoSettings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := helmClient.AddOrUpdateChartRepo(chartRepo); err != nil {
|
if err := helmClient.AddOrUpdateChartRepo(chartRepo); err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package kubespray
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"kube-forge/internal/config"
|
"kube-forge/internal/config"
|
||||||
"kube-forge/internal/logging"
|
"kube-forge/internal/logging"
|
||||||
"kube-forge/internal/python"
|
"kube-forge/internal/python"
|
||||||
@@ -15,48 +13,6 @@ import (
|
|||||||
"github.com/apenella/go-ansible/v2/pkg/playbook"
|
"github.com/apenella/go-ansible/v2/pkg/playbook"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPlaybookParameters(tags string) playbook.AnsiblePlaybookOptions {
|
|
||||||
cfg := config.GetConfig()
|
|
||||||
|
|
||||||
ansiblePlaybookOptions := playbook.AnsiblePlaybookOptions{
|
|
||||||
Inventory: filepath.Join(cfg.InventoryDir, "hosts"),
|
|
||||||
Tags: tags,
|
|
||||||
User: cfg.Credentials.User,
|
|
||||||
Become: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Credentials.PrivateKeyFile != "" {
|
|
||||||
ansiblePlaybookOptions.PrivateKey = cfg.Credentials.PrivateKeyFile
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Credentials.AskSudoPassword {
|
|
||||||
ansiblePlaybookOptions.AskBecomePass = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return ansiblePlaybookOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyK8SAdminConfig(outFile string) {
|
|
||||||
config := config.GetConfig()
|
|
||||||
var adminDefaultConfigPath = filepath.Join(config.InventoryDir, "artifacts/admin.conf")
|
|
||||||
source, err := os.Open(adminDefaultConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
destination, err := os.Create(outFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer destination.Close()
|
|
||||||
_, err = io.Copy(destination, source)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
logging.Log.Info(fmt.Sprintf("K8s admin config: %s", outFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runPlaybook(playbookPath string, tags string) {
|
func runPlaybook(playbookPath string, tags string) {
|
||||||
config := config.GetConfig()
|
config := config.GetConfig()
|
||||||
var callbackExecute execute.Executor
|
var callbackExecute execute.Executor
|
||||||
@@ -111,6 +67,6 @@ func runPlaybook(playbookPath string, tags string) {
|
|||||||
|
|
||||||
err := callbackExecute.Execute(context.Background())
|
err := callbackExecute.Execute(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
54
internal/kubespray/utilits.go
Normal file
54
internal/kubespray/utilits.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package kubespray
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"kube-forge/internal/config"
|
||||||
|
"kube-forge/internal/logging"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/apenella/go-ansible/v2/pkg/playbook"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPlaybookParameters(tags string) playbook.AnsiblePlaybookOptions {
|
||||||
|
cfg := config.GetConfig()
|
||||||
|
|
||||||
|
ansiblePlaybookOptions := playbook.AnsiblePlaybookOptions{
|
||||||
|
Inventory: filepath.Join(cfg.InventoryDir, "hosts"),
|
||||||
|
Tags: tags,
|
||||||
|
User: cfg.Credentials.User,
|
||||||
|
Become: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Credentials.PrivateKeyFile != "" {
|
||||||
|
ansiblePlaybookOptions.PrivateKey = cfg.Credentials.PrivateKeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Credentials.AskSudoPassword {
|
||||||
|
ansiblePlaybookOptions.AskBecomePass = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ansiblePlaybookOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyK8SAdminConfig(outFile string) {
|
||||||
|
config := config.GetConfig()
|
||||||
|
var adminDefaultConfigPath = filepath.Join(config.InventoryDir, "artifacts/admin.conf")
|
||||||
|
source, err := os.Open(adminDefaultConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
destination, err := os.Create(outFile)
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer destination.Close()
|
||||||
|
_, err = io.Copy(destination, source)
|
||||||
|
if err != nil {
|
||||||
|
logging.Log.Fatal(err)
|
||||||
|
}
|
||||||
|
logging.Log.Info(fmt.Sprintf("K8s admin config: %s", outFile))
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"kube-forge/internal/logging"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -22,6 +23,6 @@ func main() {
|
|||||||
pipPlatform := platforms[strings.Join([]string{os, arch}, "-")]
|
pipPlatform := platforms[strings.Join([]string{os, arch}, "-")]
|
||||||
err := pip.CreateEmbeddedPipPackages("requirements.txt", os, arch, pipPlatform, "./data/")
|
err := pip.CreateEmbeddedPipPackages("requirements.txt", os, arch, pipPlatform, "./data/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"kube-forge/internal/logging"
|
||||||
"kube-forge/internal/python/data"
|
"kube-forge/internal/python/data"
|
||||||
"kube-forge/internal/resources"
|
"kube-forge/internal/resources"
|
||||||
"os"
|
"os"
|
||||||
@@ -117,7 +118,7 @@ func NewPythonExec() *PythonExec {
|
|||||||
|
|
||||||
pythonLibFs, err := embed_util.NewEmbeddedFilesWithTmpDir(data.Data, pythonLibDir, true)
|
pythonLibFs, err := embed_util.NewEmbeddedFilesWithTmpDir(data.Data, pythonLibDir, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
pythonLibFsPath := pythonLibFs.GetExtractedPath()
|
pythonLibFsPath := pythonLibFs.GetExtractedPath()
|
||||||
resourcesFs, _ := embed_util.NewEmbeddedFilesWithTmpDir(resources.Kubespray, resourcesDir, true)
|
resourcesFs, _ := embed_util.NewEmbeddedFilesWithTmpDir(resources.Kubespray, resourcesDir, true)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func executeTemplateToString(template *template.Template, config *config.Config)
|
|||||||
templateResult := &bytes.Buffer{}
|
templateResult := &bytes.Buffer{}
|
||||||
err := template.Execute(templateResult, config)
|
err := template.Execute(templateResult, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
templateResultString := templateResult.String()
|
templateResultString := templateResult.String()
|
||||||
return templateResultString
|
return templateResultString
|
||||||
@@ -24,12 +24,12 @@ func executeTemplateToString(template *template.Template, config *config.Config)
|
|||||||
func getTemplateFromEmbedFSFolder(embedFS embed.FS, templateFile string) *template.Template {
|
func getTemplateFromEmbedFSFolder(embedFS embed.FS, templateFile string) *template.Template {
|
||||||
templateData, err := embedFS.ReadFile(templateFile)
|
templateData, err := embedFS.ReadFile(templateFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
templateDataString := string(templateData)
|
templateDataString := string(templateData)
|
||||||
template, err := template.New("tmpl").Funcs(funcMap()).Parse(templateDataString)
|
template, err := template.New("tmpl").Funcs(funcMap()).Parse(templateDataString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
@@ -50,12 +50,12 @@ func applyTemplates(templates [][2]string) {
|
|||||||
|
|
||||||
file, err := os.Create(outFile)
|
file, err := os.Create(outFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
err = tmpl.Execute(file, config)
|
err = tmpl.Execute(file, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logging.Log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
197
test-conf/manifests/HA-test.yaml
Normal file
197
test-conf/manifests/HA-test.yaml
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
---
|
||||||
|
# Namespace for testing
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: ha-test
|
||||||
|
|
||||||
|
---
|
||||||
|
# ConfigMap with application configuration
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: nginx-config
|
||||||
|
namespace: ha-test
|
||||||
|
data:
|
||||||
|
index.html: |
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>HA Cluster Test</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; margin: 40px; }
|
||||||
|
.info { background: #e7f3ff; padding: 20px; border-radius: 5px; margin: 10px 0; }
|
||||||
|
.hostname { color: #2196F3; font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Kubernetes HA Cluster Test</h1>
|
||||||
|
<div class="info">
|
||||||
|
<p><strong>Pod Hostname:</strong> <span class="hostname" id="hostname"></span></p>
|
||||||
|
<p><strong>Timestamp:</strong> <span id="timestamp"></span></p>
|
||||||
|
<p><strong>Node IP:</strong> <span id="nodeip"></span></p>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('hostname').textContent = window.location.hostname;
|
||||||
|
document.getElementById('timestamp').textContent = new Date().toISOString();
|
||||||
|
// Node IP will be shown via environment variable injection
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
---
|
||||||
|
# Deployment with multiple replicas for HA testing
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ha-test-app
|
||||||
|
namespace: ha-test
|
||||||
|
labels:
|
||||||
|
app: ha-test-app
|
||||||
|
spec:
|
||||||
|
replicas: 6 # Deploy across multiple nodes
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxUnavailable: 1
|
||||||
|
maxSurge: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ha-test-app
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ha-test-app
|
||||||
|
spec:
|
||||||
|
# Anti-affinity to spread pods across nodes
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: app
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- ha-test-app
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.25-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
name: http
|
||||||
|
env:
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: status.podIP
|
||||||
|
volumeMounts:
|
||||||
|
- name: nginx-config
|
||||||
|
mountPath: /usr/share/nginx/html
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "64Mi"
|
||||||
|
cpu: "50m"
|
||||||
|
limits:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
volumes:
|
||||||
|
- name: nginx-config
|
||||||
|
configMap:
|
||||||
|
name: nginx-config
|
||||||
|
|
||||||
|
---
|
||||||
|
# Service to expose the deployment
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ha-test-service
|
||||||
|
namespace: ha-test
|
||||||
|
labels:
|
||||||
|
app: ha-test-app
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: ha-test-app
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
type: ClusterIP
|
||||||
|
|
||||||
|
---
|
||||||
|
# NodePort Service for external access (alternative to ingress)
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ha-test-nodeport
|
||||||
|
namespace: ha-test
|
||||||
|
labels:
|
||||||
|
app: ha-test-app
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: ha-test-app
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
nodePort: 30080
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
type: NodePort
|
||||||
|
|
||||||
|
---
|
||||||
|
# HorizontalPodAutoscaler for testing scaling
|
||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: ha-test-hpa
|
||||||
|
namespace: ha-test
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: ha-test-app
|
||||||
|
minReplicas: 3
|
||||||
|
maxReplicas: 12
|
||||||
|
metrics:
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 70
|
||||||
|
|
||||||
|
---
|
||||||
|
# PodDisruptionBudget for HA
|
||||||
|
apiVersion: policy/v1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
name: ha-test-pdb
|
||||||
|
namespace: ha-test
|
||||||
|
spec:
|
||||||
|
minAvailable: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ha-test-app
|
||||||
Reference in New Issue
Block a user