init
This commit is contained in:
128
internal/k8s/kubectl.go
Normal file
128
internal/k8s/kubectl.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"deployment-manager/internal/events"
|
||||
"deployment-manager/internal/model"
|
||||
)
|
||||
|
||||
type KubectlClient struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewKubectlClient(namespace string) *KubectlClient {
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
return &KubectlClient{namespace: namespace}
|
||||
}
|
||||
|
||||
func (k *KubectlClient) ApplyManifest(ctx context.Context, eventChan chan<- *events.Event, repoID int64, manifest string) error {
|
||||
cmd := "kubectl"
|
||||
args := []string{
|
||||
"apply",
|
||||
"-f", "-",
|
||||
"--namespace", k.namespace,
|
||||
}
|
||||
|
||||
// Use kubectl with stdin for the manifest
|
||||
kubectlCmd := exec.CommandContext(ctx, cmd, args...)
|
||||
kubectlCmd.Stdin = strings.NewReader(manifest)
|
||||
|
||||
stdout, _ := kubectlCmd.StdoutPipe()
|
||||
stderr, _ := kubectlCmd.StderrPipe()
|
||||
|
||||
if err := kubectlCmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start kubectl apply: %w", err)
|
||||
}
|
||||
|
||||
// Stream output
|
||||
go streamOutput(stdout, eventChan, repoID, "kubectl")
|
||||
go streamOutput(stderr, eventChan, repoID, "kubectl")
|
||||
|
||||
return kubectlCmd.Wait()
|
||||
}
|
||||
|
||||
func (k *KubectlClient) DeleteResources(ctx context.Context, eventChan chan<- *events.Event, repoID int64, appName string) error {
|
||||
commands := [][]string{
|
||||
{"delete", "deployment", appName, "--namespace", k.namespace},
|
||||
{"delete", "service", appName, "--namespace", k.namespace},
|
||||
{"delete", "configmap", appName, "--namespace", k.namespace},
|
||||
}
|
||||
|
||||
for _, args := range commands {
|
||||
cmd := "kubectl"
|
||||
if err := runKubectlCommand(ctx, eventChan, repoID, cmd, args...); err != nil {
|
||||
// Don't fail if resources don't exist
|
||||
if !strings.Contains(err.Error(), "not found") {
|
||||
return fmt.Errorf("failed to delete resource with args %v: %w", args, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KubectlClient) ScaleDeployment(ctx context.Context, eventChan chan<- *events.Event, repoID int64, appName string, replicas int) error {
|
||||
cmd := "kubectl"
|
||||
args := []string{
|
||||
"scale",
|
||||
"deployment", appName,
|
||||
"--replicas", fmt.Sprintf("%d", replicas),
|
||||
"--namespace", k.namespace,
|
||||
}
|
||||
|
||||
return runKubectlCommand(ctx, eventChan, repoID, cmd, args...)
|
||||
}
|
||||
|
||||
func (k *KubectlClient) GetDeploymentStatus(ctx context.Context, appName string) (string, error) {
|
||||
cmd := "kubectl"
|
||||
args := []string{
|
||||
"get", "deployment", appName,
|
||||
"--namespace", k.namespace,
|
||||
"-o", "jsonpath='{.status.readyReplicas}'",
|
||||
}
|
||||
|
||||
output, err := exec.CommandContext(ctx, cmd, args...).Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get deployment status: %w", err)
|
||||
}
|
||||
|
||||
return strings.Trim(string(output), "'"), nil
|
||||
}
|
||||
|
||||
func runKubectlCommand(ctx context.Context, eventChan chan<- *events.Event, repoID int64, cmd string, args ...string) error {
|
||||
kubectlCmd := exec.CommandContext(ctx, cmd, args...)
|
||||
stdout, _ := kubectlCmd.StdoutPipe()
|
||||
stderr, _ := kubectlCmd.StderrPipe()
|
||||
|
||||
if err := kubectlCmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start kubectl command: %w", err)
|
||||
}
|
||||
|
||||
go streamOutput(stdout, eventChan, repoID, "kubectl")
|
||||
go streamOutput(stderr, eventChan, repoID, "kubectl")
|
||||
|
||||
return kubectlCmd.Wait()
|
||||
}
|
||||
|
||||
func streamOutput(r interface{ Read([]byte) (int, error) }, eventChan chan<- *events.Event, repoID int64, source string) {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
event := events.NewLogEvent(repoID, scanner.Text())
|
||||
event.Data["source"] = source
|
||||
eventChan <- event
|
||||
}
|
||||
}
|
||||
|
||||
func GetAppName(repo *model.Repo) string {
|
||||
// Generate a consistent app name based on repo
|
||||
parts := strings.Split(strings.TrimSuffix(repo.RepoURL, ".git"), "/")
|
||||
repoName := parts[len(parts)-1]
|
||||
return fmt.Sprintf("repo-%d-%s", repo.ID, strings.ToLower(repoName))
|
||||
}
|
||||
Reference in New Issue
Block a user