Skip to content

Commit 81b8978

Browse files
[POA-3041] Check all containers for env vars (#88)
This pull request fixes the bug where we consider the first container to be the main container and only check it for env vars. In some cases, like service mes, the main container won't be the first container. So we will now check for environment variables in all containers and use the container that has all the required environment variables. Changes: * Removed the unused `allRequiredEnvVarsAbsentErr` error and its corresponding handling in `StartProcessInExistingPods` and `handlePodModifyEvent` methods * Simplified the error message for `requiredEnvVarMissingErr` to "required environment variables missing" since it is not printed anywhere. * Updated the `inspectPodForEnvVars` method to retrieve UUIDs for all containers in a pod instead of just the main container * Replaced the `GetMainContainerUUID` method with `GetContainerUUIDs` to return UUIDs of all containers in a pod * Modified the test function `k8s_funcs` to handle multiple container UUIDs and print them
1 parent f9f54ee commit 81b8978

File tree

4 files changed

+129
-57
lines changed

4 files changed

+129
-57
lines changed

cmd/internal/kube/daemonset/daemonset.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,11 @@ func (d *Daemonset) StartProcessInExistingPods() error {
252252
args := NewPodArgs(pod.Name)
253253
err := d.inspectPodForEnvVars(pod, args)
254254
if err != nil {
255-
switch err {
256-
case allRequiredEnvVarsAbsentErr:
257-
printer.Debugf("None of the required env vars present, skipping pod: %s\n", pod.Name)
258-
case requiredEnvVarMissingErr:
259-
printer.Errorf("Required env var missing, skipping pod: %s\n", pod.Name)
255+
switch e := err.(type) {
256+
case *allRequiredEnvVarsAbsentError:
257+
printer.Debugf(e.Error())
258+
case *requiredEnvVarMissingError:
259+
printer.Errorf(e.Error())
260260
default:
261261
printer.Errorf("Failed to inspect pod for env vars, pod name: %s, error: %v\n", pod.Name, err)
262262
}

cmd/internal/kube/daemonset/kube_events_worker.go

Lines changed: 112 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,45 @@
11
package daemonset
22

33
import (
4+
"fmt"
5+
46
"github.com/akitasoftware/akita-libs/akid"
7+
"github.com/akitasoftware/go-utils/maps"
58
"github.com/pkg/errors"
69
"github.com/postmanlabs/postman-insights-agent/deployment"
710
"github.com/postmanlabs/postman-insights-agent/printer"
811
coreV1 "k8s.io/api/core/v1"
912
)
1013

11-
var (
12-
allRequiredEnvVarsAbsentErr = errors.New("All required environment variables are absent.")
13-
requiredEnvVarMissingErr = errors.New("One or more required environment variables are missing. " +
14-
"Ensure all the necessary environment variables are set correctly via ConfigMaps or Secrets.")
15-
)
14+
type baseEnvVarsError struct {
15+
podName string
16+
missingAttrs []string
17+
}
18+
19+
type allRequiredEnvVarsAbsentError struct {
20+
baseEnvVarsError
21+
}
22+
23+
func (e *allRequiredEnvVarsAbsentError) Error() string {
24+
errMsg := fmt.Sprintf("All required environment variables are absent. "+
25+
"PodName: %s Missing env vars: %v\n", e.podName, e.missingAttrs)
26+
return errMsg
27+
}
28+
29+
type requiredEnvVarMissingError struct {
30+
baseEnvVarsError
31+
}
32+
33+
func (e *requiredEnvVarMissingError) Error() string {
34+
errMsg := fmt.Sprintf("One or more required environment variables are missing. "+
35+
"PodName: %s Missing env vars: %v\n", e.podName, e.missingAttrs)
36+
return errMsg
37+
}
38+
39+
type requiredContainerConfig struct {
40+
projectID string
41+
apiKey string
42+
}
1643

1744
// handlePodAddEvent handles the event when a pod is added to the Kubernetes cluster.
1845
// It performs the following steps:
@@ -102,11 +129,11 @@ func (d *Daemonset) handlePodModifyEvent(pod coreV1.Pod) {
102129
printer.Debugf("Pod is running, starting api dump process, pod name: %s\n", podArgs.PodName)
103130
err := d.inspectPodForEnvVars(pod, podArgs)
104131
if err != nil {
105-
switch err {
106-
case allRequiredEnvVarsAbsentErr:
107-
printer.Debugf("None of the required env vars present, skipping pod: %s\n", pod.Name)
108-
case requiredEnvVarMissingErr:
109-
printer.Errorf("Required env var missing, skipping pod: %s\n", pod.Name)
132+
switch e := err.(type) {
133+
case *allRequiredEnvVarsAbsentError:
134+
printer.Debugf(e.Error())
135+
case *requiredEnvVarMissingError:
136+
printer.Errorf(e.Error())
110137
default:
111138
printer.Errorf("Failed to inspect pod for env vars, pod name: %s, error: %v\n", pod.Name, err)
112139
}
@@ -136,59 +163,102 @@ func (d *Daemonset) handlePodModifyEvent(pod coreV1.Pod) {
136163
// in the pod, fetches the environment variables of that container, and extracts the
137164
// necessary variables such as the project ID, API key, and environment.
138165
func (d *Daemonset) inspectPodForEnvVars(pod coreV1.Pod, podArgs *PodArgs) error {
139-
// Get the UUID of the main container in the pod
140-
containerUUID, err := d.KubeClient.GetMainContainerUUID(pod)
166+
// Get the UUIDs of all containers in the pod
167+
containerUUIDs, err := d.KubeClient.GetContainerUUIDs(pod)
141168
if err != nil {
142-
return errors.Wrapf(err, "failed to get main container UUID for pod: %s", pod.Name)
169+
return errors.Wrapf(err, "failed to get container UUIDs for pod: %s", pod.Name)
143170
}
144171

145-
// Get the environment variables of the main container
146-
envVars, err := d.CRIClient.GetEnvVars(containerUUID)
147-
if err != nil {
148-
return errors.Wrapf(err, "failed to get environment variables for pod/container : %s/%s", pod.Name, containerUUID)
172+
if len(containerUUIDs) == 0 {
173+
return errors.New("no running containers found in the pod")
149174
}
150175

151-
var (
152-
insightsProjectID akid.ServiceID
153-
insightsAPIKey string
154-
)
176+
containerConfigMap := maps.NewMap[string, requiredContainerConfig]()
155177

156-
// Extract the necessary environment variables
157-
for key, value := range envVars {
158-
switch key {
159-
case POSTMAN_INSIGHTS_PROJECT_ID:
160-
err := akid.ParseIDAs(value, &insightsProjectID)
161-
if err != nil {
162-
return errors.Wrap(err, "failed to parse project ID")
163-
}
164-
case POSTMAN_INSIGHTS_API_KEY:
165-
insightsAPIKey = value
178+
// Iterate over all containers in the pod to check for the required environment variables
179+
for _, containerUUID := range containerUUIDs {
180+
envVars, err := d.CRIClient.GetEnvVars(containerUUID)
181+
if err != nil {
182+
printer.Debugf("Failed to get environment variables for pod/container : %s/%s\n", pod.Name, containerUUID)
183+
continue
166184
}
185+
186+
containerEnvVars := requiredContainerConfig{}
187+
if projectID, exists := envVars[POSTMAN_INSIGHTS_PROJECT_ID]; exists {
188+
containerEnvVars.projectID = projectID
189+
}
190+
if apiKey, exists := envVars[POSTMAN_INSIGHTS_API_KEY]; exists {
191+
containerEnvVars.apiKey = apiKey
192+
}
193+
containerConfigMap[containerUUID] = containerEnvVars
167194
}
168195

169-
if (insightsProjectID == akid.ServiceID{}) && insightsAPIKey == "" {
170-
return allRequiredEnvVarsAbsentErr
196+
mainContainerUUID := ""
197+
mainContainerConfig := requiredContainerConfig{}
198+
mainContainerMissingAttrs := []string{}
199+
maxSetAttrs := -1
200+
201+
for uuid, config := range containerConfigMap {
202+
attrCount, missingAttrs := countSetAttributes(config)
203+
if attrCount > maxSetAttrs {
204+
maxSetAttrs = attrCount
205+
mainContainerMissingAttrs = missingAttrs
206+
mainContainerUUID = uuid
207+
mainContainerConfig = config
208+
}
171209
}
172210

173-
if (insightsProjectID == akid.ServiceID{}) {
174-
printer.Errorf("Project ID is missing, set it using the environment variable %s, pod name: %s\n", POSTMAN_INSIGHTS_PROJECT_ID, pod.Name)
175-
return requiredEnvVarMissingErr
211+
// If all required environment variables are absent, return an error
212+
if maxSetAttrs == 0 {
213+
return &allRequiredEnvVarsAbsentError{
214+
baseEnvVarsError: baseEnvVarsError{
215+
missingAttrs: mainContainerMissingAttrs,
216+
podName: pod.Name,
217+
},
218+
}
176219
}
177220

178-
if insightsAPIKey == "" {
179-
printer.Errorf("API key is missing, set it using the environment variable %s, pod name: %s\n", POSTMAN_INSIGHTS_API_KEY, pod.Name)
180-
return requiredEnvVarMissingErr
221+
// If one or more required environment variables are missing, return an error
222+
if len(mainContainerMissingAttrs) > 0 {
223+
return &requiredEnvVarMissingError{
224+
baseEnvVarsError: baseEnvVarsError{
225+
missingAttrs: mainContainerMissingAttrs,
226+
podName: pod.Name,
227+
},
228+
}
181229
}
182230

183231
// Set the trace tags for apidump process from the pod info
184232
deployment.SetK8sTraceTags(pod, podArgs.TraceTags)
185233

186-
podArgs.ContainerUUID = containerUUID
187-
podArgs.InsightsProjectID = insightsProjectID
234+
podArgs.ContainerUUID = mainContainerUUID
235+
err = akid.ParseIDAs(mainContainerConfig.projectID, &podArgs.InsightsProjectID)
236+
if err != nil {
237+
return errors.Wrap(err, "failed to parse project ID")
238+
}
188239
podArgs.PodCreds = PodCreds{
189-
InsightsAPIKey: insightsAPIKey,
240+
InsightsAPIKey: mainContainerConfig.apiKey,
190241
InsightsEnvironment: d.InsightsEnvironment,
191242
}
192243

193244
return nil
194245
}
246+
247+
// Function to count non-zero attributes in a struct
248+
func countSetAttributes(config requiredContainerConfig) (int, []string) {
249+
count := 0
250+
missingAttrs := []string{}
251+
252+
checkAttr := func(attr, name string) {
253+
if attr != "" {
254+
count++
255+
} else {
256+
missingAttrs = append(missingAttrs, name)
257+
}
258+
}
259+
260+
checkAttr(config.apiKey, POSTMAN_INSIGHTS_API_KEY)
261+
checkAttr(config.projectID, POSTMAN_INSIGHTS_PROJECT_ID)
262+
263+
return count, missingAttrs
264+
}

integrations/kube_apis/kube_apis.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,23 @@ func (kc *KubeClient) FilterPodsByContainerImage(pods []coreV1.Pod, containerIma
174174
return filteredPods, nil
175175
}
176176

177-
// GetMainContainerUUID returns the UUID of the main container of a given pod
178-
func (kc *KubeClient) GetMainContainerUUID(pod coreV1.Pod) (string, error) {
179-
if len(pod.Status.ContainerStatuses) > 0 {
180-
containerID := pod.Status.ContainerStatuses[0].ContainerID
177+
// GetContainerUUIDs returns the UUIDs of all containers in a given pod
178+
func (kc *KubeClient) GetContainerUUIDs(pod coreV1.Pod) ([]string, error) {
179+
var containerUUIDs []string
180+
181+
for _, containerStatus := range pod.Status.ContainerStatuses {
182+
containerID := containerStatus.ContainerID
181183

182184
// Extract UUID from the container ID
183185
parts := strings.Split(containerID, "://")
184186
if len(parts) == 2 {
185-
return parts[1], nil
187+
containerUUIDs = append(containerUUIDs, parts[1])
186188
} else {
187-
return "", errors.Errorf("invalid container ID: %s", containerID)
189+
printer.Debugf("invalid container ID: %s\n", containerID)
188190
}
189191
}
190192

191-
return "", errors.Errorf("no containers found for pod: %s", pod.Name)
193+
return containerUUIDs, nil
192194
}
193195

194196
// GetPodsStatus returns the statuses for list of pods

integrations/tests/kube_cri_apis/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ func k8s_funcs(kubeClient kube_apis.KubeClient) (string, error) {
9191
printer.Infof("Pod Status: %s\n", podStatuses)
9292

9393
// Get Main Container UUID
94-
containerUUID, err := kubeClient.GetMainContainerUUID(pod)
94+
containerUUIDs, err := kubeClient.GetContainerUUIDs(pod)
9595
if err != nil {
9696
return "", fmt.Errorf("failed to get main container UUID: %v", err)
9797
}
98-
printer.Infof("Main Container UUID: %s\n", containerUUID)
98+
printer.Infof("Container UUIDs: %s\n", containerUUIDs)
9999

100-
return containerUUID, nil
100+
return containerUUIDs[0], nil
101101
}
102102

103103
func cri_funcs(containerUUID string) error {

0 commit comments

Comments
 (0)