|
1 | 1 | package daemonset
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
| 5 | + |
4 | 6 | "github.com/akitasoftware/akita-libs/akid"
|
| 7 | + "github.com/akitasoftware/go-utils/maps" |
5 | 8 | "github.com/pkg/errors"
|
6 | 9 | "github.com/postmanlabs/postman-insights-agent/deployment"
|
7 | 10 | "github.com/postmanlabs/postman-insights-agent/printer"
|
8 | 11 | coreV1 "k8s.io/api/core/v1"
|
9 | 12 | )
|
10 | 13 |
|
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 | +} |
16 | 43 |
|
17 | 44 | // handlePodAddEvent handles the event when a pod is added to the Kubernetes cluster.
|
18 | 45 | // It performs the following steps:
|
@@ -102,11 +129,11 @@ func (d *Daemonset) handlePodModifyEvent(pod coreV1.Pod) {
|
102 | 129 | printer.Debugf("Pod is running, starting api dump process, pod name: %s\n", podArgs.PodName)
|
103 | 130 | err := d.inspectPodForEnvVars(pod, podArgs)
|
104 | 131 | 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()) |
110 | 137 | default:
|
111 | 138 | printer.Errorf("Failed to inspect pod for env vars, pod name: %s, error: %v\n", pod.Name, err)
|
112 | 139 | }
|
@@ -136,59 +163,102 @@ func (d *Daemonset) handlePodModifyEvent(pod coreV1.Pod) {
|
136 | 163 | // in the pod, fetches the environment variables of that container, and extracts the
|
137 | 164 | // necessary variables such as the project ID, API key, and environment.
|
138 | 165 | 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) |
141 | 168 | 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) |
143 | 170 | }
|
144 | 171 |
|
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") |
149 | 174 | }
|
150 | 175 |
|
151 |
| - var ( |
152 |
| - insightsProjectID akid.ServiceID |
153 |
| - insightsAPIKey string |
154 |
| - ) |
| 176 | + containerConfigMap := maps.NewMap[string, requiredContainerConfig]() |
155 | 177 |
|
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 |
166 | 184 | }
|
| 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 |
167 | 194 | }
|
168 | 195 |
|
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 | + } |
171 | 209 | }
|
172 | 210 |
|
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 | + } |
176 | 219 | }
|
177 | 220 |
|
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 | + } |
181 | 229 | }
|
182 | 230 |
|
183 | 231 | // Set the trace tags for apidump process from the pod info
|
184 | 232 | deployment.SetK8sTraceTags(pod, podArgs.TraceTags)
|
185 | 233 |
|
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 | + } |
188 | 239 | podArgs.PodCreds = PodCreds{
|
189 |
| - InsightsAPIKey: insightsAPIKey, |
| 240 | + InsightsAPIKey: mainContainerConfig.apiKey, |
190 | 241 | InsightsEnvironment: d.InsightsEnvironment,
|
191 | 242 | }
|
192 | 243 |
|
193 | 244 | return nil
|
194 | 245 | }
|
| 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 | +} |
0 commit comments