env: emit events for missing envvars
Fixes #465 Signed-off-by: Paulo Pires <pjpires@gmail.com>
This commit is contained in:
@@ -1,13 +1,48 @@
|
||||
package vkubelet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/client-go/tools/record"
|
||||
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReasonOptionalConfigMapNotFound is the reason used in events emitted when an optional config map is not found.
|
||||
ReasonOptionalConfigMapNotFound = "OptionalConfigMapNotFound"
|
||||
// ReasonOptionalConfigMapKeyNotFound is the reason used in events emitted when an optional config map key is not found.
|
||||
ReasonOptionalConfigMapKeyNotFound = "OptionalConfigMapKeyNotFound"
|
||||
// ReasonFailedToReadOptionalConfigMap is the reason used in events emitted when an optional config map could not be read.
|
||||
ReasonFailedToReadOptionalConfigMap = "FailedToReadOptionalConfigMap"
|
||||
|
||||
// ReasonOptionalSecretNotFound is the reason used in events emitted when an optional secret is not found.
|
||||
ReasonOptionalSecretNotFound = "OptionalSecretNotFound"
|
||||
// ReasonOptionalSecretKeyNotFound is the reason used in events emitted when an optional secret key is not found.
|
||||
ReasonOptionalSecretKeyNotFound = "OptionalSecretKeyNotFound"
|
||||
// ReasonFailedToReadOptionalSecret is the reason used in events emitted when an optional secret could not be read.
|
||||
ReasonFailedToReadOptionalSecret = "FailedToReadOptionalSecret"
|
||||
|
||||
// ReasonMandatoryConfigMapNotFound is the reason used in events emitted when an mandatory config map is not found.
|
||||
ReasonMandatoryConfigMapNotFound = "MandatoryConfigMapNotFound"
|
||||
// ReasonMandatoryConfigMapKeyNotFound is the reason used in events emitted when an mandatory config map key is not found.
|
||||
ReasonMandatoryConfigMapKeyNotFound = "MandatoryConfigMapKeyNotFound"
|
||||
// ReasonFailedToReadMandatoryConfigMap is the reason used in events emitted when an mandatory config map could not be read.
|
||||
ReasonFailedToReadMandatoryConfigMap = "FailedToReadMandatoryConfigMap"
|
||||
|
||||
// ReasonMandatorySecretNotFound is the reason used in events emitted when an mandatory secret is not found.
|
||||
ReasonMandatorySecretNotFound = "MandatorySecretNotFound"
|
||||
// ReasonMandatorySecretKeyNotFound is the reason used in events emitted when an mandatory secret key is not found.
|
||||
ReasonMandatorySecretKeyNotFound = "MandatorySecretKeyNotFound"
|
||||
// ReasonFailedToReadMandatorySecret is the reason used in events emitted when an mandatory secret could not be read.
|
||||
ReasonFailedToReadMandatorySecret = "FailedToReadMandatorySecret"
|
||||
)
|
||||
|
||||
// populateEnvironmentVariables populates Secrets and ConfigMap into environment variables
|
||||
func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
func (s *Server) populateEnvironmentVariables(ctx context.Context, pod *corev1.Pod, recorder record.EventRecorder) error {
|
||||
for _, c := range pod.Spec.Containers {
|
||||
for i, e := range c.Env {
|
||||
if e.ValueFrom != nil {
|
||||
@@ -23,15 +58,23 @@ func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
// We couldn't fetch the config map.
|
||||
// However, if the key reference is optional we should not fail.
|
||||
if optional {
|
||||
// TODO: Emit an event (the contents of which possibly depend on whether the error is "NotFound", for clarity).
|
||||
if errors.IsNotFound(err) {
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonOptionalConfigMapNotFound, "skipping optional envvar %q: configmap %q not found", e.Name, vf.Name)
|
||||
} else {
|
||||
log.G(ctx).Warnf("failed to read configmap %q: %v", vf.Name, err)
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonFailedToReadOptionalConfigMap, "skipping optional envvar %q: failed to read configmap %q", e.Name, vf.Name)
|
||||
}
|
||||
// Continue on to the next reference.
|
||||
continue
|
||||
}
|
||||
// At this point we know the key reference is mandatory.
|
||||
// Hence, we should return a meaningful error.
|
||||
if errors.IsNotFound(err) {
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonMandatoryConfigMapNotFound, "configmap %q not found", vf.Name)
|
||||
return fmt.Errorf("required configmap %q not found", vf.Name)
|
||||
}
|
||||
return fmt.Errorf("failed to fetch required configmap %q: %v", vf.Name, err)
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonFailedToReadMandatoryConfigMap, "failed to read configmap %q", vf.Name)
|
||||
return fmt.Errorf("failed to read required configmap %q: %v", vf.Name, err)
|
||||
}
|
||||
// At this point we have successfully fetched the target config map.
|
||||
// We must now try to grab the requested key.
|
||||
@@ -43,11 +86,13 @@ func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
// The requested key does not exist.
|
||||
// However, we should not fail if the key reference is optional.
|
||||
if optional {
|
||||
// TODO: Emit an event.
|
||||
// Continue on to the next reference.
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonOptionalConfigMapKeyNotFound, "skipping optional envvar %q: key %q does not exist in configmap %q", e.Name, vf.Key, vf.Name)
|
||||
continue
|
||||
}
|
||||
// At this point we know the key reference is mandatory.
|
||||
// Hence, we should fail.
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonMandatoryConfigMapKeyNotFound, "key %q does not exist in configmap %q", vf.Key, vf.Name)
|
||||
return fmt.Errorf("configmap %q doesn't contain the %q key required by pod %s", vf.Name, vf.Key, pod.Name)
|
||||
}
|
||||
// Populate the environment variable and continue on to the next reference.
|
||||
@@ -66,15 +111,23 @@ func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
// We couldn't fetch the secret.
|
||||
// However, if the key reference is optional we should not fail.
|
||||
if optional {
|
||||
// TODO: Emit an event (the contents of which possibly depend on whether the error is "NotFound", for clarity).
|
||||
if errors.IsNotFound(err) {
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonOptionalSecretNotFound, "skipping optional envvar %q: secret %q not found", e.Name, vf.Name)
|
||||
} else {
|
||||
log.G(ctx).Warnf("failed to read secret %q: %v", vf.Name, err)
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonFailedToReadOptionalSecret, "skipping optional envvar %q: failed to read secret %q", e.Name, vf.Name)
|
||||
}
|
||||
// Continue on to the next reference.
|
||||
continue
|
||||
}
|
||||
// At this point we know the key reference is mandatory.
|
||||
// Hence, we should return a meaningful error.
|
||||
if errors.IsNotFound(err) {
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonMandatorySecretNotFound, "secret %q not found", vf.Name)
|
||||
return fmt.Errorf("required secret %q not found", vf.Name)
|
||||
}
|
||||
return fmt.Errorf("failed to fetch required secret %q: %v", vf.Name, err)
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonFailedToReadMandatorySecret, "failed to read secret %q", vf.Name)
|
||||
return fmt.Errorf("failed to read secret %q: %v", vf.Name, err)
|
||||
}
|
||||
// At this point we have successfully fetched the target secret.
|
||||
// We must now try to grab the requested key.
|
||||
@@ -86,11 +139,13 @@ func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
// The requested key does not exist.
|
||||
// However, we should not fail if the key reference is optional.
|
||||
if optional {
|
||||
// TODO: Emit an event.
|
||||
// Continue on to the next reference.
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonOptionalSecretKeyNotFound, "skipping optional envvar %q: key %q does not exist in secret %q", e.Name, vf.Key, vf.Name)
|
||||
continue
|
||||
}
|
||||
// At this point we know the key reference is mandatory.
|
||||
// Hence, we should fail.
|
||||
recorder.Eventf(pod, corev1.EventTypeWarning, ReasonMandatorySecretKeyNotFound, "key %q does not exist in secret %q", vf.Key, vf.Name)
|
||||
return fmt.Errorf("secret %q doesn't contain the %q key required by pod %s", vf.Name, vf.Key, pod.Name)
|
||||
}
|
||||
// Populate the environment variable and continue on to the next reference.
|
||||
@@ -110,6 +165,5 @@ func (s *Server) populateEnvironmentVariables(pod *corev1.Pod) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
)
|
||||
@@ -26,7 +27,7 @@ func addPodAttributes(span *trace.Span, pod *corev1.Pod) {
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) createOrUpdatePod(ctx context.Context, pod *corev1.Pod) error {
|
||||
func (s *Server) createOrUpdatePod(ctx context.Context, pod *corev1.Pod, recorder record.EventRecorder) error {
|
||||
// Check if the pod is already known by the provider.
|
||||
// NOTE: Some providers return a non-nil error in their GetPod implementation when the pod is not found while some other don't.
|
||||
// Hence, we ignore the error and just act upon the pod if it is non-nil (meaning that the provider still knows about the pod).
|
||||
@@ -41,7 +42,7 @@ func (s *Server) createOrUpdatePod(ctx context.Context, pod *corev1.Pod) error {
|
||||
defer span.End()
|
||||
addPodAttributes(span, pod)
|
||||
|
||||
if err := s.populateEnvironmentVariables(pod); err != nil {
|
||||
if err := s.populateEnvironmentVariables(ctx, pod, recorder); err != nil {
|
||||
span.SetStatus(trace.Status{Code: trace.StatusCodeInvalidArgument, Message: err.Error()})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ func (pc *PodController) syncPodInProvider(ctx context.Context, pod *corev1.Pod)
|
||||
}
|
||||
|
||||
// Create or update the pod in the provider.
|
||||
if err := pc.server.createOrUpdatePod(ctx, pod); err != nil {
|
||||
if err := pc.server.createOrUpdatePod(ctx, pod, pc.recorder); err != nil {
|
||||
err := pkgerrors.Wrapf(err, "failed to sync pod %q in the provider", loggablePodName(pod))
|
||||
span.SetStatus(ocstatus.FromError(err))
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user