@@ -3,8 +3,8 @@ package kubernetes
33import (
44 "context"
55 "encoding/json"
6+ "errors"
67 "fmt"
7- "log"
88 "time"
99
1010 corev1 "k8s.io/api/core/v1"
@@ -24,64 +24,76 @@ var (
2424 ProbeImageVersion = "latest"
2525)
2626
27- func GetKubernetes (context string ) (* Kubernetes , error ) {
27+ // New returns a new Kubernetes object, connected to the given
28+ // context, or to the in-cluster API if blank.
29+ func New (context string ) (* Kubernetes , error ) {
30+ config , err := getClusterConfig (context )
31+ if err != nil {
32+ return nil , fmt .Errorf ("fetching Kubernetes configuration: %w" , err )
33+ }
34+
35+ client , err := kubernetes .NewForConfig (config )
36+ if err != nil {
37+ return nil , fmt .Errorf ("creating Kubernetes client: %w" , err )
38+ }
39+
40+ return & Kubernetes {
41+ client : client ,
42+ }, nil
43+ }
44+
45+ func getClusterConfig (kontext string ) (* rest.Config , error ) {
2846 // attempt to use config from pod service account
29- config , err := rest .InClusterConfig ()
47+ cfg , err := rest .InClusterConfig ()
3048 if err != nil {
3149 // Can be overridden by KUBECONFIG variable
3250 loadingRules := clientcmd .NewDefaultClientConfigLoadingRules ()
33- configOverride := & clientcmd.ConfigOverrides {}
34- if context != "" {
35- configOverride .CurrentContext = context
51+ configOverride := & clientcmd.ConfigOverrides {
52+ CurrentContext : kontext ,
3653 }
3754
38- config , err = clientcmd .NewNonInteractiveDeferredLoadingClientConfig (
55+ cfg , err = clientcmd .NewNonInteractiveDeferredLoadingClientConfig (
3956 loadingRules ,
4057 configOverride ,
4158 ).ClientConfig ()
42-
4359 if err != nil {
44- return nil , fmt .Errorf ("fetching Kubernetes configuration: %w" , err )
60+ return nil , fmt .Errorf ("loading Kubernetes configuration: %w" , err )
4561 }
4662 }
4763
48- client , err := kubernetes .NewForConfig (config )
49- if err != nil {
50- return nil , fmt .Errorf ("creating Kubernetes client: %w" , err )
51- }
52-
53- return & Kubernetes {
54- Client : client ,
55- Config : config ,
56- }, nil
64+ return cfg , nil
5765}
5866
5967type Kubernetes struct {
60- Config * rest.Config
61- Client kubernetes.Interface
68+ client kubernetes.Interface
6269}
6370
64- func ( k * Kubernetes ) GetPods ( ctx context. Context , namespace string ) [] string {
65- pods , err := k . Client . CoreV1 (). Pods ( namespace ). List (
66- ctx ,
67- metav1. ListOptions {} )
71+ var (
72+ errNoPodsFound = errors . New ( "no pods found" )
73+ errNoContainersInPod = errors . New ( "no containers in pod" )
74+ )
6875
76+ func (k * Kubernetes ) GetPods (ctx context.Context , namespace , selector string ) ([]corev1.Pod , error ) {
77+ pods , err := k .client .CoreV1 ().Pods (namespace ).List (ctx , metav1.ListOptions {
78+ LabelSelector : selector ,
79+ })
6980 if err != nil {
70- panic ( err . Error () )
81+ return nil , fmt . Errorf ( "listing pods for namespace %s and selector %q: %w" , namespace , selector , err )
7182 }
72- podNames := [] string {}
73- for _ , pod := range pods .Items {
74- podNames = append ( podNames , pod . Name )
83+
84+ if len ( pods .Items ) == 0 {
85+ return nil , fmt . Errorf ( "%w: namespace %s, selector %q" , errNoPodsFound , namespace , selector )
7586 }
76- return podNames
87+
88+ return pods .Items , nil
7789}
7890
79- func chooseTargetContainer (pod * corev1.Pod ) string {
91+ func chooseTargetContainer (pod * corev1.Pod ) ( string , error ) {
8092 // TODO add capability to pick container by name (currently assume 0th container)
8193 if len (pod .Spec .Containers ) == 0 {
82- log . Fatalf ( "Error: No containers in pod." )
94+ return "" , errNoContainersInPod
8395 }
84- return pod .Spec .Containers [0 ].Name
96+ return pod .Spec .Containers [0 ].Name , nil
8597}
8698
8799func (k * Kubernetes ) LaunchEphemeralContainer (ctx context.Context , pod * corev1.Pod , command []string , args []string ) (* corev1.Pod , string , error ) {
@@ -92,14 +104,19 @@ func (k *Kubernetes) LaunchEphemeralContainer(ctx context.Context, pod *corev1.P
92104
93105 ephemeralName := fmt .Sprintf ("nethax-probe-%v" , time .Now ().UnixNano ())
94106
107+ targetContainer , err := chooseTargetContainer (pod )
108+ if err != nil {
109+ return nil , "" , fmt .Errorf ("choosing target container: %w" , err )
110+ }
111+
95112 debugContainer := & corev1.EphemeralContainer {
96113 EphemeralContainerCommon : corev1.EphemeralContainerCommon {
97114 Name : ephemeralName ,
98115 Image : fmt .Sprintf ("nethax-probe:%s" , ProbeImageVersion ),
99116 Command : command ,
100117 Args : args ,
101118 },
102- TargetContainerName : chooseTargetContainer ( pod ) ,
119+ TargetContainerName : targetContainer ,
103120 }
104121
105122 debugPod := pod .DeepCopy ()
@@ -115,7 +132,7 @@ func (k *Kubernetes) LaunchEphemeralContainer(ctx context.Context, pod *corev1.P
115132 return nil , ephemeralName , fmt .Errorf ("error creating patch to add debug container: %v" , err )
116133 }
117134
118- pods := k .Client .CoreV1 ().Pods (pod .Namespace )
135+ pods := k .client .CoreV1 ().Pods (pod .Namespace )
119136 result , err := pods .Patch (ctx , pod .Name , types .StrategicMergePatchType , patch , metav1.PatchOptions {}, "ephemeralcontainers" )
120137 if err != nil {
121138 return nil , ephemeralName , fmt .Errorf ("error patching pod with debug container: %v" , err )
@@ -125,7 +142,7 @@ func (k *Kubernetes) LaunchEphemeralContainer(ctx context.Context, pod *corev1.P
125142}
126143
127144func (k * Kubernetes ) getEphemeralContainerExitStatus (ctx context.Context , pod * corev1.Pod , ephemeralContainerName string ) (int32 , error ) {
128- pod , err := k .Client .CoreV1 ().Pods (pod .Namespace ).Get (ctx , pod .Name , metav1.GetOptions {})
145+ pod , err := k .client .CoreV1 ().Pods (pod .Namespace ).Get (ctx , pod .Name , metav1.GetOptions {})
129146 if err != nil {
130147 return - 1 , err
131148 }
0 commit comments