Merge pull request #7296 from dokku/k3s-nginx-show-config

Implement nginx:show-config for k3s plugin
This commit is contained in:
Jose Diaz-Gonzalez
2024-10-21 22:09:55 -04:00
committed by GitHub
7 changed files with 151 additions and 10 deletions

View File

@@ -528,13 +528,17 @@ This plugin implements various functionality through `plugn` triggers to integra
- `enter`
- `deploy`
- healthchecks
- Due to Kubernetes limitations, only a single healthcheck is supported for each of the `liveness`, `readiness`, and `startup` healthchecks
- Due to Kubernetes limitations, content checks are not supported
- Ports specified in the `app.json` are ignored in favor of the container port on the port mapping detected
- Due to Kubernetes limitations, only a single healthcheck is supported for each of the `liveness`, `readiness`, and `startup` healthchecks
- Due to Kubernetes limitations, content checks are not supported
- Ports specified in the `app.json` are ignored in favor of the container port on the port mapping detected
- `logs`
- `nginx`
- Properties set by the `nginx` plugin will be respected, either by turning them into annotations or creating a custom server/location snippet that the `ingress-nginx` project can use.
- The `nginx:access-logs` and `nginx:error-logs` commands will fetch logs from one running `ingress-nginx` pod.
- The `nginx:show-config` command will retrieve any `server` blocks associated with a domain attached to the app from one running `ingress-nginx` pod.
- `ps:stop`
- `run`
- The `scheduler-post-run` trigger is not always triggered
- The `scheduler-post-run` trigger is not always triggered
- `run:detached`
- `run:list`

View File

@@ -158,8 +158,21 @@ cmd-nginx-show-config() {
declare cmd="nginx:show-config"
[[ "$1" == "$cmd" ]] && shift 1
declare APP="$1"
local proxy_type
verify_app_name "$APP"
proxy_type="$(plugn trigger proxy-type "$APP")"
local DOKKU_SCHEDULER=$(get_app_scheduler "$APP")
if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then
plugn trigger scheduler-proxy-config "$DOKKU_SCHEDULER" "$APP" "$proxy_type"
return $?
fi
if [[ "$proxy_type" != "nginx" ]]; then
dokku_log_fail "This command is only available for apps with the nginx proxy type"
fi
if [[ ! -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then
dokku_log_fail "No nginx.conf exists for $APP"
fi

View File

@@ -66,6 +66,7 @@ restart_nginx() {
nginx_logs() {
declare desc="display app nginx logs"
declare NGINX_LOGS_TYPE="${1#nginx:}" APP="$2"
local proxy_type
local NGINX_LOGS_TYPE=${NGINX_LOGS_TYPE%-logs}
local NGINX_LOGS_PATH="$("fn-nginx-computed-${NGINX_LOGS_TYPE}-log-path" "$APP")"
@@ -74,6 +75,8 @@ nginx_logs() {
dokku_log_fail "$NGINX_LOGS_TYPE logs are disabled for this app"
fi
proxy_type="$(plugn trigger proxy-type "$APP")"
local tail=false
local num=0
if [[ $3 == "-t" ]]; then
@@ -84,7 +87,7 @@ nginx_logs() {
local DOKKU_SCHEDULER=$(get_app_scheduler "$APP")
if [[ "$DOKKU_SCHEDULER" != "docker-local" ]]; then
plugn trigger scheduler-proxy-logs "$DOKKU_SCHEDULER" "$APP" "nginx" "$NGINX_LOGS_TYPE" "$tail" "$num"
plugn trigger scheduler-proxy-logs "$DOKKU_SCHEDULER" "$APP" "$proxy_type" "$NGINX_LOGS_TYPE" "$tail" "$num"
return $?
fi

View File

@@ -1,5 +1,5 @@
SUBCOMMANDS = subcommands/annotations:set subcommands/autoscaling-auth:set subcommands/autoscaling-auth:report subcommands/cluster-add subcommands/cluster-list subcommands/cluster-remove subcommands/initialize subcommands/labels:set subcommands/report subcommands/set subcommands/show-kubeconfig subcommands/uninstall
TRIGGERS = triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop
TRIGGERS = triggers/install triggers/post-app-clone-setup triggers/post-app-rename-setup triggers/post-delete triggers/report triggers/scheduler-app-status triggers/scheduler-deploy triggers/scheduler-enter triggers/scheduler-logs triggers/scheduler-proxy-config triggers/scheduler-proxy-logs triggers/scheduler-post-delete triggers/scheduler-run triggers/scheduler-run-list triggers/scheduler-stop
BUILD = commands subcommands triggers
PLUGIN_NAME = scheduler-k3s

View File

@@ -312,6 +312,12 @@ type ExecCommandInput struct {
// Namespace is the Kubernetes namespace
Namespace string
// Stderr is the error writer
Stderr io.Writer
// Stdout is the output writer
Stdout io.Writer
}
// ExecCommand executes a command in a Kubernetes pod
@@ -340,11 +346,26 @@ func (k KubernetesClient) ExecCommand(ctx context.Context, input ExecCommandInpu
req.Param("command", cmd)
}
var stdout io.Writer
if input.Stdout == nil {
stdout = os.Stdout
} else {
stdout = input.Stdout
}
var stderr io.Writer
if input.Stderr == nil {
stderr = os.Stderr
} else {
stderr = input.Stderr
}
t := term.TTY{
In: os.Stdin,
Out: os.Stdout,
Out: stdout,
Raw: true,
}
size := t.GetSize()
sizeQueue := t.MonitorSize(size)
@@ -356,8 +377,8 @@ func (k KubernetesClient) ExecCommand(ctx context.Context, input ExecCommandInpu
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Stdout: stdout,
Stderr: stderr,
Tty: true,
TerminalSizeQueue: sizeQueue,
})

View File

@@ -84,6 +84,11 @@ func main() {
}
err = scheduler_k3s.TriggerSchedulerLogs(scheduler, appName, processType, tail, quiet, numLines)
case "scheduler-proxy-config":
scheduler := flag.Arg(0)
appName := flag.Arg(1)
proxyType := flag.Arg(2)
err = scheduler_k3s.TriggerSchedulerProxyConfig(scheduler, appName, proxyType)
case "scheduler-proxy-logs":
var tail bool
var numLines int64

View File

@@ -1,12 +1,14 @@
package scheduler_k3s
import (
"bytes"
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
@@ -841,9 +843,102 @@ func TriggerSchedulerLogs(scheduler string, appName string, processType string,
})
}
// TriggerSchedulerProxyConfig displays nginx config for a given application
func TriggerSchedulerProxyConfig(scheduler string, appName string, proxyType string) error {
if scheduler != "k3s" || proxyType != "k3s" {
return nil
}
clientset, err := NewKubernetesClient()
if err != nil {
return fmt.Errorf("Error creating kubernetes client: %w", err)
}
pods, err := clientset.ListPods(context.Background(), ListPodsInput{
Namespace: "ingress-nginx",
LabelSelector: "app.kubernetes.io/name=ingress-nginx",
})
if err != nil {
return fmt.Errorf("Error listing pods: %w", err)
}
if len(pods) == 0 {
return errors.New("No pods found for ingress-nginx")
}
var buf bytes.Buffer
w := io.MultiWriter(&buf)
command := []string{"cat", "/etc/nginx/nginx.conf"}
err = clientset.ExecCommand(context.Background(), ExecCommandInput{
Command: command,
ContainerName: "controller",
Name: pods[0].Name,
Namespace: "ingress-nginx",
Stdout: w,
})
if err != nil {
return fmt.Errorf("Error reading nginx config: %w", err)
}
// split buffer string into lines
lines := strings.Split(buf.String(), "\n")
// get every domain block from the nginx config
// domain blocks begin with "## start server DOMAIN_NAME" and end with "## end server DOMAIN_NAME"
// each line also may have space characters at the beginning
domainBlocks := map[string][]string{}
for i, line := range lines {
strippedLine := strings.TrimSpace(line)
if strings.HasPrefix(strippedLine, "## start server ") {
domainName := strings.TrimPrefix(strippedLine, "## start server ")
domainBlocks[domainName] = []string{}
for _, nextLine := range lines[i+1:] {
nextStrippedLine := strings.TrimSpace(nextLine)
if strings.HasPrefix(nextStrippedLine, "## end server "+domainName) {
break
}
domainBlocks[domainName] = append(domainBlocks[domainName], nextLine)
}
}
}
// get all domains for this app
domains := []string{}
_, err = common.CallPlugnTrigger(common.PlugnTriggerInput{
Trigger: "domains-vhost-enabled",
Args: []string{appName},
StreamStdio: true,
})
if err == nil {
results, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
Trigger: "domains-list",
Args: []string{appName},
})
if err != nil {
return fmt.Errorf("Error getting domains for deployment: %w", err)
}
for _, domain := range strings.Split(results.StdoutContents(), "\n") {
domain = strings.TrimSpace(domain)
if domain != "" {
domains = append(domains, domain)
}
}
}
for _, domain := range domains {
if domainBlock, ok := domainBlocks[domain]; ok {
fmt.Println(strings.Join(domainBlock, "\n"))
}
}
return err
}
// TriggerSchedulerProxyLogs displays nginx logs for a given application
func TriggerSchedulerProxyLogs(scheduler string, appName string, proxyType string, logType string, tail bool, numLines int64) error {
if scheduler != "k3s" || proxyType != "nginx" {
if scheduler != "k3s" || proxyType != "k3s" {
return nil
}