refactor: move functions around and ensure we respect local versions of golang dependencies
This commit is contained in:
@@ -4,21 +4,43 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubectl/pkg/util/term"
|
||||
"k8s.io/kubernetes/pkg/client/conditions"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
)
|
||||
|
||||
type EnterPodInput struct {
|
||||
AppName string
|
||||
Clientset KubernetesClient
|
||||
Command []string
|
||||
Entrypoint string
|
||||
ProcessType string
|
||||
SelectedPod v1.Pod
|
||||
}
|
||||
|
||||
type GetPodInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
Selector string
|
||||
}
|
||||
|
||||
// StartCommandInput contains all the information needed to get the start command
|
||||
type StartCommandInput struct {
|
||||
// AppName is the name of the app
|
||||
@@ -45,6 +67,21 @@ type KubernetesClient struct {
|
||||
RestConfig rest.Config
|
||||
}
|
||||
|
||||
type WaitForPodBySelectorRunningInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
Selector string
|
||||
Timeout int
|
||||
Waiter func(ctx context.Context, clientset KubernetesClient, podName, namespace string) wait.ConditionWithContextFunc
|
||||
}
|
||||
|
||||
type WaitForPodToExistInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
RetryCount int
|
||||
Selector string
|
||||
}
|
||||
|
||||
func NewKubernetesClient() (KubernetesClient, error) {
|
||||
clientConfig := KubernetesClientConfig()
|
||||
restConf, err := clientConfig.ClientConfig()
|
||||
@@ -132,15 +169,69 @@ func createKubernetesNamespace(ctx context.Context, namespaceName string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func enterPod(ctx context.Context, input EnterPodInput) error {
|
||||
coreclient, err := corev1client.NewForConfig(&input.Clientset.RestConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating corev1 client: %w", err)
|
||||
}
|
||||
|
||||
req := coreclient.RESTClient().Post().
|
||||
Resource("pods").
|
||||
Namespace(input.SelectedPod.Namespace).
|
||||
Name(input.SelectedPod.Name).
|
||||
SubResource("exec")
|
||||
|
||||
req.Param("container", fmt.Sprintf("%s-%s", input.AppName, input.ProcessType))
|
||||
req.Param("stdin", "true")
|
||||
req.Param("stdout", "true")
|
||||
req.Param("stderr", "true")
|
||||
req.Param("tty", "true")
|
||||
|
||||
if input.Entrypoint != "" {
|
||||
req.Param("command", input.Entrypoint)
|
||||
}
|
||||
for _, cmd := range input.Command {
|
||||
req.Param("command", cmd)
|
||||
}
|
||||
|
||||
t := term.TTY{
|
||||
In: os.Stdin,
|
||||
Out: os.Stdout,
|
||||
Raw: true,
|
||||
}
|
||||
size := t.GetSize()
|
||||
sizeQueue := t.MonitorSize(size)
|
||||
|
||||
return t.Safe(func() error {
|
||||
exec, err := remotecommand.NewSPDYExecutor(&input.Clientset.RestConfig, "POST", req.URL())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating executor: %w", err)
|
||||
}
|
||||
|
||||
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Tty: true,
|
||||
TerminalSizeQueue: sizeQueue,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func extractStartCommand(input StartCommandInput) string {
|
||||
command := ""
|
||||
if input.ImageSourceType == "herokuish" {
|
||||
command = "/start " + input.ProcessType
|
||||
}
|
||||
|
||||
startCommandOverride, ok := config.Get(input.AppName, "DOKKU_START_CMD")
|
||||
if ok {
|
||||
command = startCommandOverride
|
||||
startCommandResp, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||
Trigger: "config-get",
|
||||
Args: []string{input.AppName, "DOKKU_START_CMD"},
|
||||
CaptureOutput: true,
|
||||
StreamStdio: false,
|
||||
})
|
||||
if err == nil && startCommandResp.ExitCode == 0 {
|
||||
command = startCommandResp.Stdout
|
||||
}
|
||||
|
||||
if input.ImageSourceType == "herokuish" {
|
||||
@@ -148,9 +239,14 @@ func extractStartCommand(input StartCommandInput) string {
|
||||
}
|
||||
|
||||
if input.ImageSourceType == "dockerfile" {
|
||||
startCommandOverride, ok := config.Get(input.AppName, "DOKKU_DOCKERFILE_START_CMD")
|
||||
if ok {
|
||||
command = startCommandOverride
|
||||
startCommandDockerfileResp, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||
Trigger: "config-get",
|
||||
Args: []string{input.AppName, "DOKKU_DOCKERFILE_START_CMD"},
|
||||
CaptureOutput: true,
|
||||
StreamStdio: false,
|
||||
})
|
||||
if err == nil && startCommandDockerfileResp.ExitCode == 0 {
|
||||
command = startCommandDockerfileResp.Stdout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,3 +317,74 @@ func getStartCommand(input StartCommandInput) (StartCommandOutput, error) {
|
||||
Command: fields,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getPod(ctx context.Context, input GetPodInput) (v1.PodList, error) {
|
||||
listOptions := metav1.ListOptions{LabelSelector: input.Selector}
|
||||
podList, err := input.Clientset.Client.CoreV1().Pods(input.Namespace).List(ctx, listOptions)
|
||||
return *podList, err
|
||||
}
|
||||
|
||||
func isPodReady(ctx context.Context, clientset KubernetesClient, podName, namespace string) wait.ConditionWithContextFunc {
|
||||
return func(ctx context.Context) (bool, error) {
|
||||
fmt.Printf(".") // progress bar!
|
||||
|
||||
pod, err := clientset.Client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch pod.Status.Phase {
|
||||
case v1.PodRunning:
|
||||
return true, nil
|
||||
case v1.PodFailed, v1.PodSucceeded:
|
||||
return false, conditions.ErrPodCompleted
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func waitForPodBySelectorRunning(ctx context.Context, input WaitForPodBySelectorRunningInput) error {
|
||||
podList, err := waitForPodToExist(ctx, WaitForPodToExistInput{
|
||||
Clientset: input.Clientset,
|
||||
Namespace: input.Namespace,
|
||||
RetryCount: 3,
|
||||
Selector: input.Selector,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error waiting for pod to exist: %w", err)
|
||||
}
|
||||
|
||||
if len(podList.Items) == 0 {
|
||||
return fmt.Errorf("no pods in %s with selector %s", input.Namespace, input.Selector)
|
||||
}
|
||||
|
||||
timeout := time.Duration(input.Timeout) * time.Second
|
||||
for _, pod := range podList.Items {
|
||||
if err := wait.PollUntilContextTimeout(ctx, time.Second, timeout, false, input.Waiter(ctx, input.Clientset, pod.Name, pod.Namespace)); err != nil {
|
||||
print("\n")
|
||||
return err
|
||||
}
|
||||
}
|
||||
print("\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForPodToExist(ctx context.Context, input WaitForPodToExistInput) (v1.PodList, error) {
|
||||
var podList v1.PodList
|
||||
var err error
|
||||
for i := 0; i < input.RetryCount; i++ {
|
||||
podList, err = getPod(ctx, GetPodInput{
|
||||
Clientset: input.Clientset,
|
||||
Namespace: input.Namespace,
|
||||
Selector: input.Selector,
|
||||
})
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
return podList, fmt.Errorf("Error listing pods: %w", err)
|
||||
}
|
||||
return podList, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ go 1.21
|
||||
|
||||
require (
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/dokku/dokku/plugins/config v0.0.0-20240112043630-928c388f10fa
|
||||
github.com/dokku/dokku/plugins/config v0.0.0-00010101000000-000000000000
|
||||
github.com/dokku/dokku/plugins/cron v0.0.0-00010101000000-000000000000
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/go-resty/resty/v2 v2.11.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
@@ -54,6 +55,7 @@ require (
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dokku/dokku/plugins/app-json v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
@@ -111,6 +113,7 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
@@ -123,6 +126,7 @@ require (
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rubenv/sql-migrate v1.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible // indirect
|
||||
@@ -169,4 +173,10 @@ require (
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/dokku/dokku/plugins/app-json => ../app-json
|
||||
|
||||
replace github.com/dokku/dokku/plugins/common => ../common
|
||||
|
||||
replace github.com/dokku/dokku/plugins/config => ../config
|
||||
|
||||
replace github.com/dokku/dokku/plugins/cron => ../cron
|
||||
|
||||
@@ -102,8 +102,6 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/dokku/dokku/plugins/config v0.0.0-20240112043630-928c388f10fa h1:lIscxUTyCvJSHV9oY3JBQazshpvSc7H2kEBwIZddMfc=
|
||||
github.com/dokku/dokku/plugins/config v0.0.0-20240112043630-928c388f10fa/go.mod h1:7lDnO8LtGfKX1eFKAXunqGGTqwzJNaf6V7IkKhxlN2g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -319,6 +317,8 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -373,6 +373,8 @@ github.com/rancher/dynamiclistener v0.3.5 h1:5TaIHvkDGmZKvc96Huur16zfTKOiLhDtK4S
|
||||
github.com/rancher/dynamiclistener v0.3.5/go.mod h1:dW/YF6/m2+uEyJ5VtEcd9THxda599HP6N9dSXk81+k0=
|
||||
github.com/rancher/wharfie v0.6.5 h1:Xxj/ki8d4BFeQBvyViW9jX8BqxaPlZwXvRD8BG2Mm/8=
|
||||
github.com/rancher/wharfie v0.6.5/go.mod h1:iGXn5Y9GEhsx1h/lPUXAklK4DoY4+6NOJF+Li1j248Y=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
|
||||
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0=
|
||||
|
||||
@@ -29,10 +29,6 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubectl/pkg/util/term"
|
||||
"k8s.io/kubernetes/pkg/client/conditions"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
@@ -69,25 +65,6 @@ func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerSchedulerPostDelete destroys the scheduler-k3s data for a given app container
|
||||
func TriggerSchedulerPostDelete(scheduler string, appName string) error {
|
||||
if scheduler != "k3s" {
|
||||
return nil
|
||||
}
|
||||
namespace := common.PropertyGetDefault("scheduler-k3s", appName, "namespace", "default")
|
||||
helmAgent, err := NewHelmAgent(namespace, DeployLogPrinter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating helm agent: %w", err)
|
||||
}
|
||||
|
||||
err = helmAgent.UninstallChart(fmt.Sprintf("dokku-%s", appName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error uninstalling chart: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerPostDelete destroys the scheduler-k3s data for a given app container
|
||||
func TriggerPostDelete(appName string) error {
|
||||
dataErr := common.RemoveAppDataDirectory("scheduler-k3s", appName)
|
||||
@@ -535,64 +512,6 @@ func TriggerSchedulerEnter(scheduler string, appName string, processType string,
|
||||
})
|
||||
}
|
||||
|
||||
type EnterPodInput struct {
|
||||
AppName string
|
||||
Clientset KubernetesClient
|
||||
Command []string
|
||||
Entrypoint string
|
||||
ProcessType string
|
||||
SelectedPod v1.Pod
|
||||
}
|
||||
|
||||
func enterPod(ctx context.Context, input EnterPodInput) error {
|
||||
coreclient, err := corev1client.NewForConfig(&input.Clientset.RestConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating corev1 client: %w", err)
|
||||
}
|
||||
|
||||
req := coreclient.RESTClient().Post().
|
||||
Resource("pods").
|
||||
Namespace(input.SelectedPod.Namespace).
|
||||
Name(input.SelectedPod.Name).
|
||||
SubResource("exec")
|
||||
|
||||
req.Param("container", fmt.Sprintf("%s-%s", input.AppName, input.ProcessType))
|
||||
req.Param("stdin", "true")
|
||||
req.Param("stdout", "true")
|
||||
req.Param("stderr", "true")
|
||||
req.Param("tty", "true")
|
||||
|
||||
if input.Entrypoint != "" {
|
||||
req.Param("command", input.Entrypoint)
|
||||
}
|
||||
for _, cmd := range input.Command {
|
||||
req.Param("command", cmd)
|
||||
}
|
||||
|
||||
t := term.TTY{
|
||||
In: os.Stdin,
|
||||
Out: os.Stdout,
|
||||
Raw: true,
|
||||
}
|
||||
size := t.GetSize()
|
||||
sizeQueue := t.MonitorSize(size)
|
||||
|
||||
return t.Safe(func() error {
|
||||
exec, err := remotecommand.NewSPDYExecutor(&input.Clientset.RestConfig, "POST", req.URL())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating executor: %w", err)
|
||||
}
|
||||
|
||||
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Tty: true,
|
||||
TerminalSizeQueue: sizeQueue,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// TriggerSchedulerLogs displays logs for a given application
|
||||
func TriggerSchedulerLogs(scheduler string, appName string, processType string, tail bool, quiet bool, numLines int64) error {
|
||||
if scheduler != "k3s" {
|
||||
@@ -712,59 +631,6 @@ func TriggerSchedulerLogs(scheduler string, appName string, processType string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerSchedulerStop stops an application
|
||||
func TriggerSchedulerStop(scheduler string, appName string) error {
|
||||
if scheduler != "k3s" {
|
||||
return nil
|
||||
}
|
||||
|
||||
clientset, err := NewKubernetesClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt, syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGTERM)
|
||||
go func() {
|
||||
<-signals
|
||||
cancel()
|
||||
}()
|
||||
|
||||
namespace := common.PropertyGetDefault("scheduler-k3s", appName, "namespace", "default")
|
||||
deploymentList, err := clientset.Client.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("dokku.com/app-name=%s", appName),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error listing deployments: %w", err)
|
||||
}
|
||||
|
||||
for _, deployment := range deploymentList.Items {
|
||||
processType, ok := deployment.Annotations["dokku.com/process-type"]
|
||||
if !ok {
|
||||
return fmt.Errorf("Deployment %s does not have a process type annotation", deployment.Name)
|
||||
}
|
||||
common.LogVerboseQuiet(fmt.Sprintf("Stopping %s process", processType))
|
||||
_, err := clientset.Client.AppsV1().Deployments(namespace).UpdateScale(ctx, deployment.Name, &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deployment.Name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: 0,
|
||||
},
|
||||
}, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating deployment scale: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerSchedulerRun runs a command in an ephemeral container
|
||||
func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []string) error {
|
||||
if scheduler != "k3s" {
|
||||
@@ -954,7 +820,7 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
}
|
||||
|
||||
batchJobSelector := fmt.Sprintf("batch.kubernetes.io/job-name=%s", createdJob.Name)
|
||||
podList, err := WaitForPodToExist(ctx, WaitForPodToExistInput{
|
||||
podList, err := waitForPodToExist(ctx, WaitForPodToExistInput{
|
||||
Clientset: clientset,
|
||||
Namespace: namespace,
|
||||
RetryCount: 3,
|
||||
@@ -972,7 +838,7 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
return nil
|
||||
}
|
||||
|
||||
err = WaitForPodBySelectorRunning(ctx, WaitForPodBySelectorRunningInput{
|
||||
err = waitForPodBySelectorRunning(ctx, WaitForPodBySelectorRunningInput{
|
||||
Clientset: clientset,
|
||||
Namespace: namespace,
|
||||
Selector: batchJobSelector,
|
||||
@@ -981,7 +847,7 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, conditions.ErrPodCompleted) {
|
||||
podList, podErr := GetPod(ctx, GetPodInput{
|
||||
podList, podErr := getPod(ctx, GetPodInput{
|
||||
Clientset: clientset,
|
||||
Namespace: namespace,
|
||||
Selector: batchJobSelector,
|
||||
@@ -1009,7 +875,7 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
return fmt.Errorf("Error waiting for pod to be running: %w", err)
|
||||
}
|
||||
|
||||
podList, err = GetPod(ctx, GetPodInput{
|
||||
podList, err = getPod(ctx, GetPodInput{
|
||||
Clientset: clientset,
|
||||
Namespace: namespace,
|
||||
Selector: batchJobSelector,
|
||||
@@ -1065,94 +931,74 @@ func TriggerSchedulerRun(scheduler string, appName string, envCount int, args []
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPodReady(ctx context.Context, clientset KubernetesClient, podName, namespace string) wait.ConditionWithContextFunc {
|
||||
return func(ctx context.Context) (bool, error) {
|
||||
fmt.Printf(".") // progress bar!
|
||||
|
||||
pod, err := clientset.Client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch pod.Status.Phase {
|
||||
case v1.PodRunning:
|
||||
return true, nil
|
||||
case v1.PodFailed, v1.PodSucceeded:
|
||||
return false, conditions.ErrPodCompleted
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
type WaitForPodBySelectorRunningInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
Selector string
|
||||
Timeout int
|
||||
Waiter func(ctx context.Context, clientset KubernetesClient, podName, namespace string) wait.ConditionWithContextFunc
|
||||
}
|
||||
|
||||
type WaitForPodToExistInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
RetryCount int
|
||||
Selector string
|
||||
}
|
||||
|
||||
func WaitForPodToExist(ctx context.Context, input WaitForPodToExistInput) (v1.PodList, error) {
|
||||
var podList v1.PodList
|
||||
var err error
|
||||
for i := 0; i < input.RetryCount; i++ {
|
||||
podList, err = GetPod(ctx, GetPodInput{
|
||||
Clientset: input.Clientset,
|
||||
Namespace: input.Namespace,
|
||||
Selector: input.Selector,
|
||||
})
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
// TriggerSchedulerPostDelete destroys the scheduler-k3s data for a given app container
|
||||
func TriggerSchedulerPostDelete(scheduler string, appName string) error {
|
||||
if scheduler != "k3s" {
|
||||
return nil
|
||||
}
|
||||
namespace := common.PropertyGetDefault("scheduler-k3s", appName, "namespace", "default")
|
||||
helmAgent, err := NewHelmAgent(namespace, DeployLogPrinter)
|
||||
if err != nil {
|
||||
return podList, fmt.Errorf("Error listing pods: %w", err)
|
||||
return fmt.Errorf("Error creating helm agent: %w", err)
|
||||
}
|
||||
return podList, nil
|
||||
}
|
||||
|
||||
type GetPodInput struct {
|
||||
Clientset KubernetesClient
|
||||
Namespace string
|
||||
Selector string
|
||||
}
|
||||
|
||||
func GetPod(ctx context.Context, input GetPodInput) (v1.PodList, error) {
|
||||
listOptions := metav1.ListOptions{LabelSelector: input.Selector}
|
||||
podList, err := input.Clientset.Client.CoreV1().Pods(input.Namespace).List(ctx, listOptions)
|
||||
return *podList, err
|
||||
}
|
||||
|
||||
func WaitForPodBySelectorRunning(ctx context.Context, input WaitForPodBySelectorRunningInput) error {
|
||||
podList, err := WaitForPodToExist(ctx, WaitForPodToExistInput{
|
||||
Clientset: input.Clientset,
|
||||
Namespace: input.Namespace,
|
||||
RetryCount: 3,
|
||||
Selector: input.Selector,
|
||||
})
|
||||
err = helmAgent.UninstallChart(fmt.Sprintf("dokku-%s", appName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error waiting for pod to exist: %w", err)
|
||||
return fmt.Errorf("Error uninstalling chart: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerSchedulerStop stops an application
|
||||
func TriggerSchedulerStop(scheduler string, appName string) error {
|
||||
if scheduler != "k3s" {
|
||||
return nil
|
||||
}
|
||||
|
||||
clientset, err := NewKubernetesClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt, syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGTERM)
|
||||
go func() {
|
||||
<-signals
|
||||
cancel()
|
||||
}()
|
||||
|
||||
namespace := common.PropertyGetDefault("scheduler-k3s", appName, "namespace", "default")
|
||||
deploymentList, err := clientset.Client.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("dokku.com/app-name=%s", appName),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error listing deployments: %w", err)
|
||||
}
|
||||
|
||||
for _, deployment := range deploymentList.Items {
|
||||
processType, ok := deployment.Annotations["dokku.com/process-type"]
|
||||
if !ok {
|
||||
return fmt.Errorf("Deployment %s does not have a process type annotation", deployment.Name)
|
||||
}
|
||||
common.LogVerboseQuiet(fmt.Sprintf("Stopping %s process", processType))
|
||||
_, err := clientset.Client.AppsV1().Deployments(namespace).UpdateScale(ctx, deployment.Name, &autoscalingv1.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deployment.Name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: autoscalingv1.ScaleSpec{
|
||||
Replicas: 0,
|
||||
},
|
||||
}, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating deployment scale: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(podList.Items) == 0 {
|
||||
return fmt.Errorf("no pods in %s with selector %s", input.Namespace, input.Selector)
|
||||
}
|
||||
|
||||
timeout := time.Duration(input.Timeout) * time.Second
|
||||
for _, pod := range podList.Items {
|
||||
if err := wait.PollUntilContextTimeout(ctx, time.Second, timeout, false, input.Waiter(ctx, input.Clientset, pod.Name, pod.Namespace)); err != nil {
|
||||
print("\n")
|
||||
return err
|
||||
}
|
||||
}
|
||||
print("\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user