diff --git a/pkg/credentialprovider/plugin/metrics.go b/pkg/credentialprovider/plugin/metrics.go new file mode 100644 index 00000000000..1e83bbc80d5 --- /dev/null +++ b/pkg/credentialprovider/plugin/metrics.go @@ -0,0 +1,64 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package plugin + +import ( + "sync" + + "k8s.io/component-base/metrics" + "k8s.io/component-base/metrics/legacyregistry" +) + +const ( + kubeletCredentialProviderPluginErrorsKey = "kubelet_credential_provider_plugin_errors" + kubeletCredentialProviderPluginDurationKey = "kubelet_credential_provider_plugin_duration" + KubeletSubsystem = "kubelet" +) + +var ( + registerOnce sync.Once + + kubeletCredentialProviderPluginErrors = metrics.NewCounterVec( + &metrics.CounterOpts{ + Subsystem: KubeletSubsystem, + Name: kubeletCredentialProviderPluginErrorsKey, + Help: "Number of errors from credential provider plugin", + StabilityLevel: metrics.ALPHA, + }, + []string{"plugin_name"}, + ) + + kubeletCredentialProviderPluginDuration = metrics.NewHistogramVec( + &metrics.HistogramOpts{ + Subsystem: KubeletSubsystem, + Name: kubeletCredentialProviderPluginDurationKey, + Help: "Duration of execution in seconds for credential provider plugin", + Buckets: metrics.DefBuckets, + StabilityLevel: metrics.ALPHA, + }, + []string{"plugin_name"}, + ) +) + +// registerMetrics registers credential provider metrics. +func registerMetrics() { + registerOnce.Do(func() { + legacyregistry.MustRegister(kubeletCredentialProviderPluginErrors) + legacyregistry.MustRegister(kubeletCredentialProviderPluginDuration) + + }) +} diff --git a/pkg/credentialprovider/plugin/plugin.go b/pkg/credentialprovider/plugin/plugin.go index 57cb52590c7..f5be9e43eed 100644 --- a/pkg/credentialprovider/plugin/plugin.go +++ b/pkg/credentialprovider/plugin/plugin.go @@ -86,6 +86,9 @@ func RegisterCredentialProviderPlugins(pluginConfigFile, pluginBinDir string) er return fmt.Errorf("failed to validate credential provider config: %v", errs.ToAggregate()) } + // Register metrics for credential providers + registerMetrics() + for _, provider := range credentialProviderConfig.Providers { pluginBin := filepath.Join(pluginBinDir, provider.Name) if _, err := os.Stat(pluginBin); err != nil { @@ -398,14 +401,8 @@ func (e *execPlugin) ExecPlugin(ctx context.Context, image string) (*credentialp // also, this behaviour is inline with Credential Provider Config spec cmd.Env = mergeEnvVars(e.environ(), configEnvVars) - err = cmd.Run() - if ctx.Err() != nil { - return nil, fmt.Errorf("error execing credential provider plugin %s for image %s: %w", e.name, image, ctx.Err()) - } - - if err != nil { - klog.V(2).Infof("Error execing credential provider plugin, stderr: %v", stderr.String()) - return nil, fmt.Errorf("error execing credential provider plugin %s for image %s: %w", e.name, image, err) + if err = e.runPlugin(ctx, cmd, image); err != nil { + return nil, err } data = stdout.Bytes() @@ -428,6 +425,22 @@ func (e *execPlugin) ExecPlugin(ctx context.Context, image string) (*credentialp return response, nil } +func (e *execPlugin) runPlugin(ctx context.Context, cmd *exec.Cmd, image string) error { + startTime := time.Now() + defer kubeletCredentialProviderPluginDuration.WithLabelValues(e.name).Observe(time.Since(startTime).Seconds()) + + err := cmd.Run() + if ctx.Err() != nil { + kubeletCredentialProviderPluginErrors.WithLabelValues(e.name).Inc() + return fmt.Errorf("error execing credential provider plugin %s for image %s: %w", e.name, image, ctx.Err()) + } + if err != nil { + kubeletCredentialProviderPluginErrors.WithLabelValues(e.name).Inc() + return fmt.Errorf("error execing credential provider plugin %s for image %s: %w", e.name, image, err) + } + return nil +} + // encodeRequest encodes the internal CredentialProviderRequest type into the v1alpha1 version in json func (e *execPlugin) encodeRequest(request *credentialproviderapi.CredentialProviderRequest) ([]byte, error) { data, err := runtime.Encode(e.encoder, request)