wormhole/bridge/e2e/k8s.go

150 lines
3.4 KiB
Go

package e2e
import (
"bytes"
"context"
"fmt"
"log"
"strings"
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
)
const (
TiltDefaultNamespace = "wormhole" // hardcoded in Tiltfile
)
func getk8sClient() *kubernetes.Clientset {
config, err := getk8sConfig()
c, err := kubernetes.NewForConfig(config)
if err != nil {
glog.Errorln(err)
}
return c
}
func getk8sConfig() (*rest.Config, error) {
// Load local default kubeconfig.
rules := clientcmd.NewDefaultClientConfigLoadingRules()
rules.DefaultClientConfig = &clientcmd.DefaultClientConfig
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.ClientConfigLoader(rules), nil).ClientConfig()
}
func hasPodReadyCondition(conditions []corev1.PodCondition) bool {
for _, condition := range conditions {
if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue {
return true
}
}
return false
}
func waitForPods(ctx context.Context, c *kubernetes.Clientset, want []string) {
found := make(map[string]bool)
ctx, cancel := context.WithCancel(ctx)
watchlist := cache.NewListWatchFromClient(
c.CoreV1().RESTClient(),
"pods",
TiltDefaultNamespace,
fields.Everything(),
)
handle := func(pod *corev1.Pod) {
ready := hasPodReadyCondition(pod.Status.Conditions)
log.Printf("pod added/changed: %s is %s, ready: %v", pod.Name, pod.Status.Phase, ready)
if ready {
found[pod.Name] = true
}
missing := 0
for _, v := range want {
if found[v] == false {
missing += 1
}
}
if missing == 0 {
cancel()
}
}
_, controller := cache.NewInformer(
watchlist,
&corev1.Pod{},
0,
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { handle(obj.(*corev1.Pod)) },
UpdateFunc: func(oldObj, newObj interface{}) { handle(newObj.(*corev1.Pod)) },
},
)
controller.Run(ctx.Done())
}
func executeCommandInPod(ctx context.Context, c *kubernetes.Clientset, podName string, container string, cmd []string) ([]byte, error) {
p, err := c.CoreV1().Pods(TiltDefaultNamespace).Get(ctx, podName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get pod %s: %w", p, err)
}
req := c.CoreV1().RESTClient().Post().
Resource("pods").
Name(podName).
Namespace(TiltDefaultNamespace).
SubResource("exec")
req = req.VersionedParams(&corev1.PodExecOptions{
Stdin: false,
Stdout: true,
Stderr: true,
TTY: false,
Container: container,
Command: cmd,
}, scheme.ParameterCodec)
config, err := getk8sConfig()
if err != nil {
return nil, fmt.Errorf("failed to get config: %w", err)
}
exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
if err != nil {
return nil, fmt.Errorf("failed to init executor: %w", err)
}
var (
execOut bytes.Buffer
execErr bytes.Buffer
)
err = exec.Stream(remotecommand.StreamOptions{
Stdout: &execOut,
Stderr: &execErr,
Tty: false,
})
log.Printf("command: %s", strings.Join(cmd, " "))
if execErr.Len() > 0 {
log.Printf("stderr: %s", execErr.String())
}
if err != nil {
return nil, fmt.Errorf("failed to execute remote command: %w", err)
}
return execOut.Bytes(), nil
}