Improve kubectl plugin

This commit is contained in:
Alex Kursell 2019-03-12 12:52:23 -04:00
parent ef0b1633e2
commit 9d62ec97de
14 changed files with 1117 additions and 464 deletions

View file

@ -0,0 +1,84 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package backends
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
var pod, deployment *string
cmd := &cobra.Command{
Use: "backends",
Short: "Inspect the dynamic backend information of an ingress-nginx instance",
RunE: func(cmd *cobra.Command, args []string) error {
backend, err := cmd.Flags().GetString("backend")
if err != nil {
return err
}
onlyList, err := cmd.Flags().GetBool("list")
if err != nil {
return err
}
if onlyList && backend != "" {
return fmt.Errorf("--list and --backend cannot both be specified")
}
util.PrintError(backends(flags, *pod, *deployment, backend, onlyList))
return nil
},
}
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
cmd.Flags().String("backend", "", "Output only the information for the given backend")
cmd.Flags().Bool("list", false, "Output a newline-separated list of backend names")
return cmd
}
func backends(flags *genericclioptions.ConfigFlags, podName string, deployment string, backend string, onlyList bool) error {
var command []string
if onlyList {
command = []string{"/dbg", "backends", "list"}
} else if backend != "" {
command = []string{"/dbg", "backends", "get", backend}
} else {
command = []string{"/dbg", "backends", "all"}
}
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
out, err := kubectl.PodExecString(flags, &pod, command)
if err != nil {
return err
}
fmt.Printf(out)
return nil
}

View file

@ -0,0 +1,70 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package certs
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
var pod, deployment *string
cmd := &cobra.Command{
Use: "certs",
Short: "Output the certificate data stored in an ingress-nginx pod",
RunE: func(cmd *cobra.Command, args []string) error {
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
util.PrintError(certs(flags, *pod, *deployment, host))
return nil
},
}
cmd.Flags().String("host", "", "Get the cert for this hostname")
cobra.MarkFlagRequired(cmd.Flags(), "host")
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
return cmd
}
func certs(flags *genericclioptions.ConfigFlags, podName string, deployment string, host string) error {
command := []string{"/dbg", "certs", "get", host}
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
out, err := kubectl.PodExecString(flags, &pod, command)
if err != nil {
return err
}
fmt.Print(out)
return nil
}

View file

@ -0,0 +1,78 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conf
import (
"fmt"
"github.com/spf13/cobra"
"strings"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
"k8s.io/ingress-nginx/internal/nginx"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
var pod, deployment *string
cmd := &cobra.Command{
Use: "conf",
Short: "Inspect the generated nginx.conf",
RunE: func(cmd *cobra.Command, args []string) error {
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
util.PrintError(conf(flags, host, *pod, *deployment))
return nil
},
}
cmd.Flags().String("host", "", "Print just the server block with this hostname")
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
return cmd
}
func conf(flags *genericclioptions.ConfigFlags, host string, podName string, deployment string) error {
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
nginxConf, err := kubectl.PodExecString(flags, &pod, []string{"/dbg", "conf"})
if err != nil {
return err
}
if host != "" {
block, err := nginx.GetServerBlock(nginxConf, host)
if err != nil {
return err
}
fmt.Println(strings.TrimRight(strings.Trim(block, " \n"), " \n\t"))
} else {
fmt.Print(nginxConf)
}
return nil
}

View file

@ -0,0 +1,72 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
opts := execFlags{}
var pod, deployment *string
cmd := &cobra.Command{
Use: "exec",
Short: "Execute a command inside an ingress-nginx pod",
RunE: func(cmd *cobra.Command, args []string) error {
util.PrintError(exec(flags, *pod, *deployment, args, opts))
return nil
},
}
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
cmd.Flags().BoolVarP(&opts.TTY, "tty", "t", false, "Stdin is a TTY")
cmd.Flags().BoolVarP(&opts.Stdin, "stdin", "i", false, "Pass stdin to the container")
return cmd
}
type execFlags struct {
TTY bool
Stdin bool
}
func exec(flags *genericclioptions.ConfigFlags, podName string, deployment string, cmd []string, opts execFlags) error {
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
args := []string{"exec"}
if opts.TTY {
args = append(args, "-t")
}
if opts.Stdin {
args = append(args, "-i")
}
args = append(args, []string{"-n", pod.Namespace, pod.Name, "--"}...)
args = append(args, cmd...)
return kubectl.Exec(flags, args)
}

View file

@ -0,0 +1,60 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package general
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
var pod, deployment *string
cmd := &cobra.Command{
Use: "general",
Short: "Inspect the other dynamic ingress-nginx information",
RunE: func(cmd *cobra.Command, args []string) error {
util.PrintError(general(flags, *pod, *deployment))
return nil
},
}
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
return cmd
}
func general(flags *genericclioptions.ConfigFlags, podName string, deployment string) error {
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
out, err := kubectl.PodExecString(flags, &pod, []string{"/dbg", "general"})
if err != nil {
return err
}
fmt.Print(out)
return nil
}

View file

@ -0,0 +1,58 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package info
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "info",
Short: "Show information about the ingress-nginx service",
RunE: func(cmd *cobra.Command, args []string) error {
service, err := cmd.Flags().GetString("service")
if err != nil {
return err
}
util.PrintError(info(flags, service))
return nil
},
}
cmd.Flags().String("service", util.DefaultIngressServiceName, "The name of the ingress-nginx service")
return cmd
}
func info(flags *genericclioptions.ConfigFlags, serviceName string) error {
service, err := request.GetServiceByName(flags, serviceName, nil)
if err != nil {
return err
}
fmt.Printf("Service cluster IP address: %v\n", service.Spec.ClusterIP)
fmt.Printf("LoadBalancer IP|CNAME: %v\n", service.Spec.LoadBalancerIP)
return nil
}

View file

@ -0,0 +1,217 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ingresses
import (
"fmt"
"github.com/spf13/cobra"
"os"
"text/tabwriter"
"k8s.io/api/extensions/v1beta1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "ingresses",
Aliases: []string{"ingress", "ing"},
Short: "Provide a short summary of all of the ingress definitions",
RunE: func(cmd *cobra.Command, args []string) error {
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
allNamespaces, err := cmd.Flags().GetBool("all-namespaces")
if err != nil {
return err
}
util.PrintError(ingresses(flags, host, allNamespaces))
return nil
},
}
cmd.Flags().String("host", "", "Show just the ingress definitions for this hostname")
cmd.Flags().Bool("all-namespaces", false, "Find ingress definitions from all namespaces")
return cmd
}
func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces bool) error {
var namespace string
if allNamespaces {
namespace = ""
} else {
namespace = util.GetNamespace(flags)
}
ingresses, err := request.GetIngressDefinitions(flags, namespace)
if err != nil {
return err
}
rows := getIngressRows(&ingresses)
if host != "" {
rowsWithHost := make([]ingressRow, 0)
for _, row := range rows {
if row.Host == host {
rowsWithHost = append(rowsWithHost, row)
}
}
rows = rowsWithHost
}
printer := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', 0)
defer printer.Flush()
if allNamespaces {
fmt.Fprintln(printer, "NAMESPACE\tINGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT\tENDPOINTS")
} else {
fmt.Fprintln(printer, "INGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT\tENDPOINTS")
}
for _, row := range rows {
var tlsMsg string
if row.TLS {
tlsMsg = "YES"
} else {
tlsMsg = "NO"
}
numEndpoints, err := request.GetNumEndpoints(flags, row.Namespace, row.ServiceName)
if err != nil {
return err
}
if numEndpoints == nil {
row.NumEndpoints = "N/A"
} else {
row.NumEndpoints = fmt.Sprint(*numEndpoints)
}
if allNamespaces {
fmt.Fprintf(printer, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", row.Namespace, row.IngressName, row.Host+row.Path, row.Address, tlsMsg, row.ServiceName, row.ServicePort, row.NumEndpoints)
} else {
fmt.Fprintf(printer, "%v\t%v\t%v\t%v\t%v\t%v\t%v\n", row.IngressName, row.Host+row.Path, row.Address, tlsMsg, row.ServiceName, row.ServicePort, row.NumEndpoints)
}
}
return nil
}
type ingressRow struct {
Namespace string
IngressName string
Host string
Path string
TLS bool
ServiceName string
ServicePort string
Address string
NumEndpoints string
}
func getIngressRows(ingresses *[]v1beta1.Ingress) []ingressRow {
rows := make([]ingressRow, 0)
for _, ing := range *ingresses {
address := ""
for _, lbIng := range ing.Status.LoadBalancer.Ingress {
if len(lbIng.IP) > 0 {
address = address + lbIng.IP + ","
}
if len(lbIng.Hostname) > 0 {
address = address + lbIng.Hostname + ","
}
}
if len(address) > 0 {
address = address[:len(address)-1]
}
tlsHosts := make(map[string]struct{})
for _, tls := range ing.Spec.TLS {
for _, host := range tls.Hosts {
tlsHosts[host] = struct{}{}
}
}
defaultBackendService := ""
defaultBackendPort := ""
if ing.Spec.Backend != nil {
defaultBackendService = ing.Spec.Backend.ServiceName
defaultBackendPort = ing.Spec.Backend.ServicePort.String()
}
// Handle catch-all ingress
if len(ing.Spec.Rules) == 0 && len(defaultBackendService) > 0 {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: "*",
ServiceName: defaultBackendService,
ServicePort: defaultBackendPort,
Address: address,
}
rows = append(rows, row)
continue
}
for _, rule := range ing.Spec.Rules {
_, hasTLS := tlsHosts[rule.Host]
//Handle ingress with no paths
if rule.HTTP == nil {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: rule.Host,
Path: "",
TLS: hasTLS,
ServiceName: defaultBackendService,
ServicePort: defaultBackendPort,
Address: address,
}
rows = append(rows, row)
continue
}
for _, path := range rule.HTTP.Paths {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: rule.Host,
Path: path.Path,
TLS: hasTLS,
ServiceName: path.Backend.ServiceName,
ServicePort: path.Backend.ServicePort.String(),
Address: address,
}
rows = append(rows, row)
}
}
}
return rows
}

View file

@ -0,0 +1,108 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logs
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
o := logsFlags{}
var pod, deployment *string
cmd := &cobra.Command{
Use: "logs",
Short: "Get the kubernetes logs for an ingress-nginx pod",
RunE: func(cmd *cobra.Command, args []string) error {
util.PrintError(logs(flags, *pod, *deployment, o))
return nil
},
}
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
cmd.Flags().BoolVarP(&o.Follow, "follow", "f", o.Follow, "Specify if the logs should be streamed.")
cmd.Flags().BoolVar(&o.Timestamps, "timestamps", o.Timestamps, "Include timestamps on each line in the log output")
cmd.Flags().Int64Var(&o.LimitBytes, "limit-bytes", o.LimitBytes, "Maximum bytes of logs to return. Defaults to no limit.")
cmd.Flags().BoolVarP(&o.Previous, "previous", "p", o.Previous, "If true, print the logs for the previous instance of the container in a pod if it exists.")
cmd.Flags().Int64Var(&o.Tail, "tail", o.Tail, "Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.")
cmd.Flags().StringVar(&o.SinceTime, "since-time", o.SinceTime, "Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")
cmd.Flags().StringVar(&o.SinceSeconds, "since", o.SinceSeconds, "Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.")
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on.")
return cmd
}
type logsFlags struct {
SinceTime string
SinceSeconds string
Follow bool
Previous bool
Timestamps bool
LimitBytes int64
Tail int64
Selector string
}
func (o *logsFlags) toStrings() []string {
r := []string{}
if o.SinceTime != "" {
r = append(r, "--since-time", o.SinceTime)
}
if o.SinceSeconds != "" {
r = append(r, "--since", o.SinceSeconds)
}
if o.Follow {
r = append(r, "--follow")
}
if o.Previous {
r = append(r, "--previous")
}
if o.Timestamps {
r = append(r, "--timestamps")
}
if o.LimitBytes != 0 {
r = append(r, "--limit-bytes", fmt.Sprintf("%v", o.LimitBytes))
}
if o.Tail != 0 {
r = append(r, "--tail", fmt.Sprintf("%v", o.Tail))
}
if o.Selector != "" {
r = append(r, "--selector", o.Selector)
}
return r
}
func logs(flags *genericclioptions.ConfigFlags, podName string, deployment string, opts logsFlags) error {
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
cmd := []string{"logs", "-n", pod.Namespace, pod.Name}
cmd = append(cmd, opts.toStrings()...)
return kubectl.Exec(flags, cmd)
}

View file

@ -0,0 +1,53 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ssh
import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/ingress-nginx/cmd/plugin/kubectl"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
// CreateCommand creates and returns this cobra subcommand
func CreateCommand(flags *genericclioptions.ConfigFlags) *cobra.Command {
var pod, deployment *string
cmd := &cobra.Command{
Use: "ssh",
Short: "ssh into a running ingress-nginx pod",
RunE: func(cmd *cobra.Command, args []string) error {
util.PrintError(ssh(flags, *pod, *deployment))
return nil
},
}
pod = util.AddPodFlag(cmd)
deployment = util.AddDeploymentFlag(cmd)
return cmd
}
func ssh(flags *genericclioptions.ConfigFlags, podName string, deployment string) error {
pod, err := request.ChoosePod(flags, podName, deployment)
if err != nil {
return err
}
return kubectl.Exec(flags, []string{"exec", "-it", "-n", pod.Namespace, pod.Name, "--", "/bin/bash"})
}

View file

@ -0,0 +1,132 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"bytes"
"fmt"
"io"
apiv1 "k8s.io/api/core/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"os"
"os/exec"
"strings"
"syscall"
)
// PodExecString takes a pod and a command, uses kubectl exec to run the command in the pod
// and returns stdout as a string
func PodExecString(flags *genericclioptions.ConfigFlags, pod *apiv1.Pod, args []string) (string, error) {
args = append([]string{"exec", "-n", pod.Namespace, pod.Name}, args...)
return ExecToString(flags, args)
}
// ExecToString runs a kubectl subcommand and returns stdout as a string
func ExecToString(flags *genericclioptions.ConfigFlags, args []string) (string, error) {
kArgs := getKubectlConfigFlags(flags)
kArgs = append(kArgs, args...)
buf := bytes.NewBuffer(make([]byte, 0))
err := execToWriter(append([]string{"kubectl"}, kArgs...), buf)
if err != nil {
return "", err
}
return buf.String(), nil
}
// Exec replaces the current process with a kubectl invocation
func Exec(flags *genericclioptions.ConfigFlags, args []string) error {
kArgs := getKubectlConfigFlags(flags)
kArgs = append(kArgs, args...)
return execCommand(append([]string{"kubectl"}, kArgs...))
}
// Replaces the currently running process with the given command
func execCommand(args []string) error {
path, err := exec.LookPath(args[0])
if err != nil {
return err
}
args[0] = path
env := os.Environ()
return syscall.Exec(path, args, env)
}
// Runs a command and returns stdout
func execToWriter(args []string, writer io.Writer) error {
cmd := exec.Command(args[0], args[1:]...)
op, err := cmd.StdoutPipe()
if err != nil {
return err
}
go io.Copy(writer, op)
err = cmd.Run()
if err != nil {
return err
}
return nil
}
// getKubectlConfigFlags serializes the parsed flag struct back into a series of command line args
// that can then be passed to kubectl. The mirror image of
// https://github.com/kubernetes/cli-runtime/blob/master/pkg/genericclioptions/config_flags.go#L251
func getKubectlConfigFlags(flags *genericclioptions.ConfigFlags) []string {
out := []string{}
o := &out
appendStringFlag(o, flags.KubeConfig, "kubeconfig")
appendStringFlag(o, flags.CacheDir, "cache-dir")
appendStringFlag(o, flags.CertFile, "client-certificate")
appendStringFlag(o, flags.KeyFile, "client-key")
appendStringFlag(o, flags.BearerToken, "token")
appendStringFlag(o, flags.Impersonate, "as")
appendStringArrayFlag(o, flags.ImpersonateGroup, "as-group")
appendStringFlag(o, flags.Username, "username")
appendStringFlag(o, flags.Password, "password")
appendStringFlag(o, flags.ClusterName, "cluster")
appendStringFlag(o, flags.AuthInfoName, "user")
//appendStringFlag(o, flags.Namespace, "namespace")
appendStringFlag(o, flags.Context, "context")
appendStringFlag(o, flags.APIServer, "server")
appendBoolFlag(o, flags.Insecure, "insecure-skip-tls-verify")
appendStringFlag(o, flags.CAFile, "certificate-authority")
appendStringFlag(o, flags.Timeout, "request-timeout")
return out
}
func appendStringFlag(out *[]string, in *string, flag string) {
if in != nil && *in != "" {
*out = append(*out, fmt.Sprintf("--%v=%v", flag, *in))
}
}
func appendBoolFlag(out *[]string, in *bool, flag string) {
if in != nil {
*out = append(*out, fmt.Sprintf("--%v=%v", flag, *in))
}
}
func appendStringArrayFlag(out *[]string, in *[]string, flag string) {
if in != nil && len(*in) > 0 {
*out = append(*out, fmt.Sprintf("--%v=%v'", flag, strings.Join(*in, ",")))
}
}

View file

@ -20,18 +20,22 @@ import (
"fmt"
"github.com/spf13/cobra"
"os"
"strings"
"text/tabwriter"
"k8s.io/api/extensions/v1beta1"
"k8s.io/cli-runtime/pkg/genericclioptions"
//Just importing this is supposed to allow cloud authentication
// eg GCP, AWS, Azure ...
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/ingress-nginx/cmd/plugin/request"
"k8s.io/ingress-nginx/cmd/plugin/util"
"k8s.io/ingress-nginx/internal/nginx"
"k8s.io/ingress-nginx/cmd/plugin/commands/backends"
"k8s.io/ingress-nginx/cmd/plugin/commands/certs"
"k8s.io/ingress-nginx/cmd/plugin/commands/conf"
"k8s.io/ingress-nginx/cmd/plugin/commands/exec"
"k8s.io/ingress-nginx/cmd/plugin/commands/general"
"k8s.io/ingress-nginx/cmd/plugin/commands/info"
"k8s.io/ingress-nginx/cmd/plugin/commands/ingresses"
"k8s.io/ingress-nginx/cmd/plugin/commands/logs"
"k8s.io/ingress-nginx/cmd/plugin/commands/ssh"
)
func main() {
@ -44,373 +48,18 @@ func main() {
flags := genericclioptions.NewConfigFlags()
flags.AddFlags(rootCmd.PersistentFlags())
ingCmd := &cobra.Command{
Use: "ingresses",
Aliases: []string{"ingress", "ing"},
Short: "Provide a short summary of all of the ingress definitions",
RunE: func(cmd *cobra.Command, args []string) error {
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
allNamespaces, err := cmd.Flags().GetBool("all-namespaces")
if err != nil {
return err
}
util.PrintError(ingresses(flags, host, allNamespaces))
return nil
},
}
ingCmd.Flags().String("host", "", "Show just the ingress definitions for this hostname")
ingCmd.Flags().Bool("all-namespaces", false, "Find ingress definitions from all namespaces")
rootCmd.AddCommand(ingCmd)
confCmd := &cobra.Command{
Use: "conf",
Short: "Inspect the generated nginx.conf",
RunE: func(cmd *cobra.Command, args []string) error {
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
pod, err := cmd.Flags().GetString("pod")
if err != nil {
return err
}
util.PrintError(conf(flags, host, pod))
return nil
},
}
confCmd.Flags().String("host", "", "Print just the server block with this hostname")
confCmd.Flags().String("pod", "", "Query a particular ingress-nginx pod")
rootCmd.AddCommand(confCmd)
generalCmd := &cobra.Command{
Use: "general",
Short: "Inspect the other dynamic ingress-nginx information",
RunE: func(cmd *cobra.Command, args []string) error {
pod, err := cmd.Flags().GetString("pod")
if err != nil {
return err
}
util.PrintError(general(flags, pod))
return nil
},
}
generalCmd.Flags().String("pod", "", "Query a particular ingress-nginx pod")
rootCmd.AddCommand(generalCmd)
infoCmd := &cobra.Command{
Use: "info",
Short: "Show information about the ingress-nginx service",
RunE: func(cmd *cobra.Command, args []string) error {
util.PrintError(info(flags))
return nil
},
}
rootCmd.AddCommand(infoCmd)
backendsCmd := &cobra.Command{
Use: "backends",
Short: "Inspect the dynamic backend information of an ingress-nginx instance",
RunE: func(cmd *cobra.Command, args []string) error {
pod, err := cmd.Flags().GetString("pod")
if err != nil {
return err
}
backend, err := cmd.Flags().GetString("backend")
if err != nil {
return err
}
onlyList, err := cmd.Flags().GetBool("list")
if err != nil {
return err
}
if onlyList && backend != "" {
return fmt.Errorf("--list and --backend cannot both be specified")
}
util.PrintError(backends(flags, pod, backend, onlyList))
return nil
},
}
backendsCmd.Flags().String("pod", "", "Query a particular ingress-nginx pod")
backendsCmd.Flags().String("backend", "", "Output only the information for the given backend")
backendsCmd.Flags().Bool("list", false, "Output a newline-separated list of backend names")
rootCmd.AddCommand(backendsCmd)
certsCmd := &cobra.Command{
Use: "certs",
Short: "Output the certificate data stored in an ingress-nginx pod",
RunE: func(cmd *cobra.Command, args []string) error {
pod, err := cmd.Flags().GetString("pod")
if err != nil {
return err
}
host, err := cmd.Flags().GetString("host")
if err != nil {
return err
}
util.PrintError(certs(flags, pod, host))
return nil
},
}
certsCmd.Flags().String("host", "", "Get the cert for this hostname")
certsCmd.Flags().String("pod", "", "Query a particular ingress-nginx pod")
cobra.MarkFlagRequired(certsCmd.Flags(), "host")
rootCmd.AddCommand(certsCmd)
rootCmd.AddCommand(ingresses.CreateCommand(flags))
rootCmd.AddCommand(conf.CreateCommand(flags))
rootCmd.AddCommand(general.CreateCommand(flags))
rootCmd.AddCommand(backends.CreateCommand(flags))
rootCmd.AddCommand(info.CreateCommand(flags))
rootCmd.AddCommand(certs.CreateCommand(flags))
rootCmd.AddCommand(logs.CreateCommand(flags))
rootCmd.AddCommand(exec.CreateCommand(flags))
rootCmd.AddCommand(ssh.CreateCommand(flags))
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func certs(flags *genericclioptions.ConfigFlags, pod string, host string) error {
command := []string{"/dbg", "certs", "get", host}
var out string
var err error
if pod != "" {
out, err = request.NamedPodExec(flags, pod, command)
} else {
out, err = request.IngressPodExec(flags, command)
}
if err != nil {
return err
}
fmt.Print(out)
return nil
}
func info(flags *genericclioptions.ConfigFlags) error {
service, err := request.GetIngressService(flags)
if err != nil {
return err
}
fmt.Printf("Service cluster IP address: %v\n", service.Spec.ClusterIP)
fmt.Printf("LoadBalancer IP|CNAME: %v\n", service.Spec.LoadBalancerIP)
return nil
}
func backends(flags *genericclioptions.ConfigFlags, pod string, backend string, onlyList bool) error {
var command []string
if onlyList {
command = []string{"/dbg", "backends", "list"}
} else if backend != "" {
command = []string{"/dbg", "backends", "get", backend}
} else {
command = []string{"/dbg", "backends", "all"}
}
var out string
var err error
if pod != "" {
out, err = request.NamedPodExec(flags, pod, command)
} else {
out, err = request.IngressPodExec(flags, command)
}
if err != nil {
return err
}
fmt.Print(out)
return nil
}
func general(flags *genericclioptions.ConfigFlags, pod string) error {
var general string
var err error
if pod != "" {
general, err = request.NamedPodExec(flags, pod, []string{"/dbg", "general"})
} else {
general, err = request.IngressPodExec(flags, []string{"/dbg", "general"})
}
if err != nil {
return err
}
fmt.Print(general)
return nil
}
func ingresses(flags *genericclioptions.ConfigFlags, host string, allNamespaces bool) error {
var namespace string
if allNamespaces {
namespace = ""
} else {
namespace = util.GetNamespace(flags)
}
ingresses, err := request.GetIngressDefinitions(flags, namespace)
if err != nil {
return err
}
rows := getIngressRows(&ingresses)
if host != "" {
rowsWithHost := make([]ingressRow, 0)
for _, row := range rows {
if row.Host == host {
rowsWithHost = append(rowsWithHost, row)
}
}
rows = rowsWithHost
}
printer := tabwriter.NewWriter(os.Stdout, 6, 4, 3, ' ', 0)
defer printer.Flush()
if allNamespaces {
fmt.Fprintln(printer, "NAMESPACE\tINGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT")
} else {
fmt.Fprintln(printer, "INGRESS NAME\tHOST+PATH\tADDRESSES\tTLS\tSERVICE\tSERVICE PORT")
}
for _, row := range rows {
var tlsMsg string
if row.TLS {
tlsMsg = "YES"
} else {
tlsMsg = "NO"
}
if allNamespaces {
fmt.Fprintf(printer, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t\n", row.Namespace, row.IngressName, row.Host+row.Path, row.Address, tlsMsg, row.ServiceName, row.ServicePort)
} else {
fmt.Fprintf(printer, "%v\t%v\t%v\t%v\t%v\t%v\t\n", row.IngressName, row.Host+row.Path, row.Address, tlsMsg, row.ServiceName, row.ServicePort)
}
}
return nil
}
func conf(flags *genericclioptions.ConfigFlags, host string, pod string) error {
var nginxConf string
var err error
if pod != "" {
nginxConf, err = request.NamedPodExec(flags, pod, []string{"/dbg", "conf"})
} else {
nginxConf, err = request.IngressPodExec(flags, []string{"/dbg", "conf"})
}
if err != nil {
return err
}
if host != "" {
block, err := nginx.GetServerBlock(nginxConf, host)
if err != nil {
return err
}
fmt.Println(strings.TrimRight(strings.Trim(block, " \n"), " \n\t"))
} else {
fmt.Print(nginxConf)
}
return nil
}
type ingressRow struct {
Namespace string
IngressName string
Host string
Path string
TLS bool
ServiceName string
ServicePort string
Address string
}
func getIngressRows(ingresses *[]v1beta1.Ingress) []ingressRow {
rows := make([]ingressRow, 0)
for _, ing := range *ingresses {
address := ""
for _, lbIng := range ing.Status.LoadBalancer.Ingress {
if len(lbIng.IP) > 0 {
address = address + lbIng.IP + ","
}
if len(lbIng.Hostname) > 0 {
address = address + lbIng.Hostname + ","
}
}
if len(address) > 0 {
address = address[:len(address)-1]
}
tlsHosts := make(map[string]struct{})
for _, tls := range ing.Spec.TLS {
for _, host := range tls.Hosts {
tlsHosts[host] = struct{}{}
}
}
defaultBackendService := ""
defaultBackendPort := ""
if ing.Spec.Backend != nil {
defaultBackendService = ing.Spec.Backend.ServiceName
defaultBackendPort = ing.Spec.Backend.ServicePort.String()
}
// Handle catch-all ingress
if len(ing.Spec.Rules) == 0 && len(defaultBackendService) > 0 {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: "*",
ServiceName: defaultBackendService,
ServicePort: defaultBackendPort,
Address: address,
}
rows = append(rows, row)
continue
}
for _, rule := range ing.Spec.Rules {
_, hasTLS := tlsHosts[rule.Host]
//Handle ingress with no paths
if rule.HTTP == nil {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: rule.Host,
Path: "",
TLS: hasTLS,
ServiceName: defaultBackendService,
ServicePort: defaultBackendPort,
Address: address,
}
rows = append(rows, row)
continue
}
for _, path := range rule.HTTP.Paths {
row := ingressRow{
Namespace: ing.Namespace,
IngressName: ing.Name,
Host: rule.Host,
Path: path.Path,
TLS: hasTLS,
ServiceName: path.Backend.ServiceName,
ServicePort: path.Backend.ServicePort.String(),
Address: address,
}
rows = append(rows, row)
}
}
}
return rows
}

View file

@ -17,116 +17,188 @@ limitations under the License.
package request
import (
"bytes"
"fmt"
apiv1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes/scheme"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
extensions "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/ingress-nginx/cmd/plugin/util"
)
const (
ingressPodName = "nginx-ingress-controller"
ingressServiceName = "ingress-nginx"
)
// ChoosePod finds a pod either by deployment or by name
func ChoosePod(flags *genericclioptions.ConfigFlags, podName string, deployment string) (apiv1.Pod, error) {
if podName != "" {
return GetNamedPod(flags, podName)
}
// NamedPodExec finds a pod with the given name, executes a command inside it, and returns stdout
func NamedPodExec(flags *genericclioptions.ConfigFlags, podName string, cmd []string) (string, error) {
return GetDeploymentPod(flags, deployment)
}
// GetNamedPod finds a pod with the given name
func GetNamedPod(flags *genericclioptions.ConfigFlags, name string) (apiv1.Pod, error) {
allPods, err := getPods(flags)
if err != nil {
return "", err
return apiv1.Pod{}, err
}
for _, pod := range allPods {
if pod.Name == podName {
return podExec(flags, &pod, cmd)
if pod.Name == name {
return pod, nil
}
}
return "", fmt.Errorf("Pod %v not found in namespace %v", podName, util.GetNamespace(flags))
return apiv1.Pod{}, fmt.Errorf("Pod %v not found in namespace %v", name, util.GetNamespace(flags))
}
// IngressPodExec finds an ingress-nginx pod in the given namespace, executes a command inside it, and returns stdout
func IngressPodExec(flags *genericclioptions.ConfigFlags, cmd []string) (string, error) {
ings, err := getIngressPods(flags)
// GetDeploymentPod finds a pod from a given deployment
func GetDeploymentPod(flags *genericclioptions.ConfigFlags, deployment string) (apiv1.Pod, error) {
ings, err := getDeploymentPods(flags, deployment)
if err != nil {
return "", err
return apiv1.Pod{}, err
}
if len(ings) == 0 {
return "", fmt.Errorf("No ingress-nginx pods found in namespace %v", util.GetNamespace(flags))
return apiv1.Pod{}, fmt.Errorf("No pods for deployment %v found in namespace %v", deployment, util.GetNamespace(flags))
}
return podExec(flags, &ings[0], cmd)
return ings[0], nil
}
func podExec(flags *genericclioptions.ConfigFlags, pod *apiv1.Pod, cmd []string) (string, error) {
config, err := flags.ToRESTConfig()
// GetIngressDefinitions returns an array of Ingress resource definitions
func GetIngressDefinitions(flags *genericclioptions.ConfigFlags, namespace string) ([]v1beta1.Ingress, error) {
rawConfig, err := flags.ToRESTConfig()
if err != nil {
return "", err
return make([]v1beta1.Ingress, 0), err
}
client, err := corev1.NewForConfig(config)
api, err := extensions.NewForConfig(rawConfig)
if err != nil {
return "", err
return make([]v1beta1.Ingress, 0), err
}
namespace, _, err := flags.ToRawKubeConfigLoader().Namespace()
pods, err := api.Ingresses(namespace).List(metav1.ListOptions{})
if err != nil {
return "", err
return make([]v1beta1.Ingress, 0), err
}
restClient := client.RESTClient()
req := restClient.Post().
Resource("pods").
Name(pod.Name).
Namespace(namespace).
SubResource("exec").
Param("container", ingressPodName)
req.VersionedParams(&apiv1.PodExecOptions{
Container: ingressPodName,
Command: cmd,
Stdin: false,
Stdout: true,
Stderr: false,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
if err != nil {
return "", err
}
stdout := bytes.NewBuffer(make([]byte, 0))
err = exec.Stream(remotecommand.StreamOptions{
Stdout: stdout,
})
return stdout.String(), err
return pods.Items, nil
}
func getIngressPods(flags *genericclioptions.ConfigFlags) ([]apiv1.Pod, error) {
pods, err := getPods(flags)
// GetNumEndpoints counts the number of endpoints for the service with the given name
func GetNumEndpoints(flags *genericclioptions.ConfigFlags, namespace string, serviceName string) (*int, error) {
endpoints, err := GetEndpointsByName(flags, namespace, serviceName)
if err != nil {
return make([]apiv1.Pod, 0), err
return nil, err
}
ingressPods := make([]apiv1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Name == ingressPodName {
ingressPods = append(ingressPods, pod)
if endpoints == nil {
return nil, nil
}
ret := 0
for _, subset := range endpoints.Subsets {
ret += len(subset.Addresses)
}
return &ret, nil
}
// GetEndpointsByName returns the endpoints for the service with the given name
func GetEndpointsByName(flags *genericclioptions.ConfigFlags, namespace string, name string) (*apiv1.Endpoints, error) {
allEndpoints, err := getEndpoints(flags, namespace)
if err != nil {
return nil, err
}
for _, endpoints := range allEndpoints {
if endpoints.Name == name {
return &endpoints, nil
}
}
return ingressPods, nil
return nil, nil
}
var endpointsCache = make(map[string]*[]apiv1.Endpoints)
func getEndpoints(flags *genericclioptions.ConfigFlags, namespace string) ([]apiv1.Endpoints, error) {
cachedEndpoints, ok := endpointsCache[namespace]
if ok {
return *cachedEndpoints, nil
}
if namespace != "" {
tryAllNamespacesEndpointsCache(flags)
}
cachedEndpoints = tryFilteringEndpointsFromAllNamespacesCache(flags, namespace)
if cachedEndpoints != nil {
return *cachedEndpoints, nil
}
rawConfig, err := flags.ToRESTConfig()
if err != nil {
return nil, err
}
api, err := corev1.NewForConfig(rawConfig)
if err != nil {
return nil, err
}
endpointsList, err := api.Endpoints(namespace).List(metav1.ListOptions{})
if err != nil {
return nil, err
}
endpoints := endpointsList.Items
endpointsCache[namespace] = &endpoints
return endpoints, nil
}
func tryAllNamespacesEndpointsCache(flags *genericclioptions.ConfigFlags) {
_, ok := endpointsCache[""]
if !ok {
_, err := getEndpoints(flags, "")
if err != nil {
endpointsCache[""] = nil
}
}
}
func tryFilteringEndpointsFromAllNamespacesCache(flags *genericclioptions.ConfigFlags, namespace string) *[]apiv1.Endpoints {
allEndpoints, _ := endpointsCache[""]
if allEndpoints != nil {
endpoints := make([]apiv1.Endpoints, 0)
for _, thisEndpoints := range *allEndpoints {
if thisEndpoints.Namespace == namespace {
endpoints = append(endpoints, thisEndpoints)
}
}
endpointsCache[namespace] = &endpoints
return &endpoints
}
return nil
}
// GetServiceByName finds and returns the service definition with the given name
func GetServiceByName(flags *genericclioptions.ConfigFlags, name string, services *[]apiv1.Service) (apiv1.Service, error) {
if services == nil {
servicesArray, err := getServices(flags)
if err != nil {
return apiv1.Service{}, err
}
services = &servicesArray
}
for _, svc := range *services {
if svc.Name == name {
return svc, nil
}
}
return apiv1.Service{}, fmt.Errorf("Could not find service %v in namespace %v", name, util.GetNamespace(flags))
}
func getPods(flags *genericclioptions.ConfigFlags) ([]apiv1.Pod, error) {
@ -150,40 +222,20 @@ func getPods(flags *genericclioptions.ConfigFlags) ([]apiv1.Pod, error) {
return pods.Items, nil
}
// GetIngressDefinitions returns an array of Ingress resource definitions
func GetIngressDefinitions(flags *genericclioptions.ConfigFlags, namespace string) ([]v1beta1.Ingress, error) {
rawConfig, err := flags.ToRESTConfig()
func getDeploymentPods(flags *genericclioptions.ConfigFlags, deployment string) ([]apiv1.Pod, error) {
pods, err := getPods(flags)
if err != nil {
return make([]v1beta1.Ingress, 0), err
return make([]apiv1.Pod, 0), err
}
api, err := extensions.NewForConfig(rawConfig)
if err != nil {
return make([]v1beta1.Ingress, 0), err
}
pods, err := api.Ingresses(namespace).List(metav1.ListOptions{})
if err != nil {
return make([]v1beta1.Ingress, 0), err
}
return pods.Items, nil
}
// GetIngressService finds and returns the ingress-nginx service definition
func GetIngressService(flags *genericclioptions.ConfigFlags) (apiv1.Service, error) {
services, err := getServices(flags)
if err != nil {
return apiv1.Service{}, err
}
for _, svc := range services {
if svc.Name == ingressServiceName {
return svc, nil
ingressPods := make([]apiv1.Pod, 0)
for _, pod := range pods {
if pod.Spec.Containers[0].Name == deployment {
ingressPods = append(ingressPods, pod)
}
}
return apiv1.Service{}, fmt.Errorf("Could not find service %v in namespace %v", ingressServiceName, util.GetNamespace(flags))
return ingressPods, nil
}
func getServices(flags *genericclioptions.ConfigFlags) ([]apiv1.Service, error) {

View file

@ -18,11 +18,17 @@ package util
import (
"fmt"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
// The default deployment and service names for ingress-nginx
const (
DefaultIngressDeploymentName = "nginx-ingress-controller"
DefaultIngressServiceName = "ingress-nginx"
)
// PrintError receives an error value and prints it if it exists
func PrintError(e error) {
if e != nil {
@ -45,6 +51,20 @@ func printOrError(s string, e error) error {
return nil
}
// AddPodFlag adds a --pod flag to a cobra command
func AddPodFlag(cmd *cobra.Command) *string {
v := ""
cmd.Flags().StringVar(&v, "pod", "", "Query a particular ingress-nginx pod")
return &v
}
// AddDeploymentFlag adds a --deployment flag to a cobra command
func AddDeploymentFlag(cmd *cobra.Command) *string {
v := ""
cmd.Flags().StringVar(&v, "deployment", DefaultIngressDeploymentName, "The name of the ingress-nginx deployment")
return &v
}
// GetNamespace takes a set of kubectl flag values and returns the namespace we should be operating in
func GetNamespace(flags *genericclioptions.ConfigFlags) string {
namespace, _, err := flags.ToRawKubeConfigLoader().Namespace()

View file

@ -91,7 +91,7 @@ func NewPostStatusRequest(path, contentType string, data interface{}) (int, []by
// GetServerBlock takes an nginx.conf file and a host and tries to find the server block for that host
func GetServerBlock(conf string, host string) (string, error) {
startMsg := fmt.Sprintf("## start server %v", host)
startMsg := fmt.Sprintf("## start server %v\n", host)
endMsg := fmt.Sprintf("## end server %v", host)
blockStart := strings.Index(conf, startMsg)