| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- /*
- Copyright © The ESO 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
- https://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 infisical
- import (
- "context"
- "errors"
- "fmt"
- infisicalSdk "github.com/infisical/go-sdk"
- kclient "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
- "github.com/external-secrets/external-secrets/providers/v1/infisical/constants"
- "github.com/external-secrets/external-secrets/runtime/esutils"
- "github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
- "github.com/external-secrets/external-secrets/runtime/metrics"
- )
- const (
- machineIdentityLoginViaUniversalAuth = "MachineIdentityLoginViaUniversalAuth"
- machineIdentityLoginViaAzureAuth = "MachineIdentityLoginViaAzureAuth"
- machineIdentityLoginViaGCPIDTokenAuth = "MachineIdentityLoginViaGcpIdTokenAuth"
- machineIdentityLoginViaGcpServiceAccountAuth = "MachineIdentityLoginViaGcpServiceAccountAuth"
- machineIdentityLoginViaJwtAuth = "MachineIdentityLoginViaJwtAuth"
- machineIdentityLoginViaLdapAuth = "MachineIdentityLoginViaLdapAuth"
- machineIdentityLoginViaOciAuth = "MachineIdentityLoginViaOciAuth"
- machineIdentityLoginViaKubernetesAuth = "MachineIdentityLoginViaKubernetesAuth"
- machineIdentityLoginViaAwsAuth = "MachineIdentityLoginViaAwsAuth"
- machineIdentityLoginViaTokenAuth = "MachineIdentityLoginViaTokenAuth"
- revokeAccessToken = "RevokeAccessToken"
- )
- const errSecretDataFormat = "failed to get secret data identityId %w"
- // Provider implements the Infisical external secrets provider.
- type Provider struct {
- cancelSdkClient context.CancelFunc
- sdkClient infisicalSdk.InfisicalClientInterface
- apiScope *ClientScope
- authMethod string
- }
- // ClientScope represents the scope configuration for an Infisical client.
- type ClientScope struct {
- EnvironmentSlug string
- ProjectSlug string
- Recursive bool
- SecretPath string
- ExpandSecretReferences bool
- }
- // https://github.com/external-secrets/external-secrets/issues/644
- var _ esv1.SecretsClient = &Provider{}
- var _ esv1.Provider = &Provider{}
- // Capabilities returns the provider's supported capabilities.
- func (p *Provider) Capabilities() esv1.SecretStoreCapabilities {
- return esv1.SecretStoreReadOnly
- }
- func performUniversalAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- universalAuthCredentials := infisicalSpec.Auth.UniversalAuthCredentials
- clientID, err := GetStoreSecretData(ctx, store, kube, namespace, universalAuthCredentials.ClientID)
- if err != nil {
- return err
- }
- clientSecret, err := GetStoreSecretData(ctx, store, kube, namespace, universalAuthCredentials.ClientSecret)
- if err != nil {
- return err
- }
- _, err = sdkClient.Auth().UniversalAuthLogin(clientID, clientSecret)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaUniversalAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via universal auth %w", err)
- }
- return nil
- }
- func performAzureAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- azureAuthCredentials := infisicalSpec.Auth.AzureAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, azureAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf("failed to get secret data id %w", err)
- }
- resource := ""
- if azureAuthCredentials.Resource.Name != "" {
- resource, err = GetStoreSecretData(ctx, store, kube, namespace, azureAuthCredentials.Resource)
- if err != nil {
- return fmt.Errorf("failed to get secret data resource %w", err)
- }
- }
- _, err = sdkClient.Auth().AzureAuthLogin(identityID, resource)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaAzureAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via azure auth %w", err)
- }
- return nil
- }
- func performGcpIDTokenAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- gcpIDTokenAuthCredentials := infisicalSpec.Auth.GcpIDTokenAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, gcpIDTokenAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- _, err = sdkClient.Auth().GcpIdTokenAuthLogin(identityID)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaGCPIDTokenAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via gcp id token auth %w", err)
- }
- return nil
- }
- func performGcpIamAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- gcpIamAuthCredentials := infisicalSpec.Auth.GcpIamAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, gcpIamAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- serviceAccountKeyFilePath, err := GetStoreSecretData(ctx, store, kube, namespace, gcpIamAuthCredentials.ServiceAccountKeyFilePath)
- if err != nil {
- return fmt.Errorf("failed to get secret data serviceAccountKeyFilePath %w", err)
- }
- _, err = sdkClient.Auth().GcpIamAuthLogin(identityID, serviceAccountKeyFilePath)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaGcpServiceAccountAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via gcp iam auth %w", err)
- }
- return nil
- }
- func performJwtAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- jwtAuthCredentials := infisicalSpec.Auth.JwtAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, jwtAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- jwt, err := GetStoreSecretData(ctx, store, kube, namespace, jwtAuthCredentials.JWT)
- if err != nil {
- return fmt.Errorf("failed to get secret data jwt %w", err)
- }
- _, err = sdkClient.Auth().JwtAuthLogin(identityID, jwt)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaJwtAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via jwt auth %w", err)
- }
- return nil
- }
- func performLdapAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- ldapAuthCredentials := infisicalSpec.Auth.LdapAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, ldapAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- ldapPassword, err := GetStoreSecretData(ctx, store, kube, namespace, ldapAuthCredentials.LDAPPassword)
- if err != nil {
- return fmt.Errorf("failed to get secret data ldapPassword %w", err)
- }
- ldapUsername, err := GetStoreSecretData(ctx, store, kube, namespace, ldapAuthCredentials.LDAPUsername)
- if err != nil {
- return fmt.Errorf("failed to get secret data ldapUsername %w", err)
- }
- _, err = sdkClient.Auth().LdapAuthLogin(identityID, ldapPassword, ldapUsername)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaLdapAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via ldap auth %w", err)
- }
- return nil
- }
- func performOciAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- ociAuthCredentials := infisicalSpec.Auth.OciAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- privateKey, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.PrivateKey)
- if err != nil {
- return fmt.Errorf("failed to get secret data privateKey %w", err)
- }
- var privateKeyPassphrase *string
- if ociAuthCredentials.PrivateKeyPassphrase.Name != "" {
- passphrase, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.PrivateKeyPassphrase)
- if err != nil {
- return fmt.Errorf("failed to get secret data privateKeyPassphrase %w", err)
- }
- privateKeyPassphrase = &passphrase
- }
- fingerprint, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.Fingerprint)
- if err != nil {
- return fmt.Errorf("failed to get secret data fingerprint %w", err)
- }
- userID, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.UserID)
- if err != nil {
- return fmt.Errorf("failed to get secret data userId %w", err)
- }
- tenancyID, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.TenancyID)
- if err != nil {
- return fmt.Errorf("failed to get secret data tenancyId %w", err)
- }
- region, err := GetStoreSecretData(ctx, store, kube, namespace, ociAuthCredentials.Region)
- if err != nil {
- return fmt.Errorf("failed to get secret data region %w", err)
- }
- _, err = sdkClient.Auth().OciAuthLogin(infisicalSdk.OciAuthLoginOptions{
- IdentityID: identityID,
- PrivateKey: privateKey,
- Passphrase: privateKeyPassphrase,
- Fingerprint: fingerprint,
- UserID: userID,
- TenancyID: tenancyID,
- Region: region,
- })
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaOciAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via oci auth %w", err)
- }
- return nil
- }
- func performKubernetesAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- kubernetesAuthCredentials := infisicalSpec.Auth.KubernetesAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, kubernetesAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- serviceAccountTokenPath := ""
- if kubernetesAuthCredentials.ServiceAccountTokenPath.Name != "" {
- serviceAccountTokenPath, err = GetStoreSecretData(ctx, store, kube, namespace, kubernetesAuthCredentials.ServiceAccountTokenPath)
- if err != nil {
- return fmt.Errorf("failed to get secret data serviceAccountTokenPath %w", err)
- }
- }
- _, err = sdkClient.Auth().KubernetesAuthLogin(identityID, serviceAccountTokenPath)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaKubernetesAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via kubernetes auth %w", err)
- }
- return nil
- }
- func performAwsAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- awsAuthCredentials := infisicalSpec.Auth.AwsAuthCredentials
- identityID, err := GetStoreSecretData(ctx, store, kube, namespace, awsAuthCredentials.IdentityID)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- _, err = sdkClient.Auth().AwsIamAuthLogin(identityID)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaAwsAuth, err)
- if err != nil {
- return fmt.Errorf("failed to authenticate via aws auth %w", err)
- }
- return nil
- }
- func performTokenAuthLogin(
- ctx context.Context,
- store esv1.GenericStore,
- infisicalSpec *esv1.InfisicalProvider,
- sdkClient infisicalSdk.InfisicalClientInterface,
- kube kclient.Client,
- namespace string,
- ) error {
- tokenAuthCredentials := infisicalSpec.Auth.TokenAuthCredentials
- accessToken, err := GetStoreSecretData(ctx, store, kube, namespace, tokenAuthCredentials.AccessToken)
- if err != nil {
- return fmt.Errorf(errSecretDataFormat, err)
- }
- sdkClient.Auth().SetAccessToken(accessToken)
- metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaTokenAuth, err)
- return nil
- }
- // NewClient creates a new Infisical client.
- func (p *Provider) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.SecretsClient, error) {
- storeSpec := store.GetSpec()
- if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Infisical == nil {
- return nil, errors.New("invalid infisical store")
- }
- infisicalSpec := storeSpec.Provider.Infisical
- // Fetch CA certificate if configured
- var caCertificate string
- if len(infisicalSpec.CABundle) > 0 || infisicalSpec.CAProvider != nil {
- caCert, err := esutils.FetchCACertFromSource(ctx, esutils.CreateCertOpts{
- CABundle: infisicalSpec.CABundle,
- CAProvider: infisicalSpec.CAProvider,
- StoreKind: store.GetObjectKind().GroupVersionKind().Kind,
- Namespace: namespace,
- Client: kube,
- })
- if err != nil {
- return nil, fmt.Errorf("failed to get CA certificate: %w", err)
- }
- if caCert != nil {
- caCertificate = string(caCert)
- }
- }
- ctx, cancelSdkClient := context.WithCancel(ctx)
- sdkClient := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{
- SiteUrl: infisicalSpec.HostAPI,
- CaCertificate: caCertificate,
- })
- secretPath := infisicalSpec.SecretsScope.SecretsPath
- if secretPath == "" {
- secretPath = "/"
- }
- var loginFn func(ctx context.Context, store esv1.GenericStore, infisicalSpec *esv1.InfisicalProvider, sdkClient infisicalSdk.InfisicalClientInterface, kube kclient.Client, namespace string) error
- var authMethod string
- switch {
- case infisicalSpec.Auth.UniversalAuthCredentials != nil:
- loginFn = performUniversalAuthLogin
- authMethod = machineIdentityLoginViaUniversalAuth
- case infisicalSpec.Auth.AzureAuthCredentials != nil:
- loginFn = performAzureAuthLogin
- authMethod = machineIdentityLoginViaAzureAuth
- case infisicalSpec.Auth.GcpIDTokenAuthCredentials != nil:
- loginFn = performGcpIDTokenAuthLogin
- authMethod = machineIdentityLoginViaGCPIDTokenAuth
- case infisicalSpec.Auth.GcpIamAuthCredentials != nil:
- loginFn = performGcpIamAuthLogin
- authMethod = machineIdentityLoginViaGcpServiceAccountAuth
- case infisicalSpec.Auth.JwtAuthCredentials != nil:
- loginFn = performJwtAuthLogin
- authMethod = machineIdentityLoginViaJwtAuth
- case infisicalSpec.Auth.LdapAuthCredentials != nil:
- loginFn = performLdapAuthLogin
- authMethod = machineIdentityLoginViaLdapAuth
- case infisicalSpec.Auth.OciAuthCredentials != nil:
- loginFn = performOciAuthLogin
- authMethod = machineIdentityLoginViaOciAuth
- case infisicalSpec.Auth.KubernetesAuthCredentials != nil:
- loginFn = performKubernetesAuthLogin
- authMethod = machineIdentityLoginViaKubernetesAuth
- case infisicalSpec.Auth.AwsAuthCredentials != nil:
- loginFn = performAwsAuthLogin
- authMethod = machineIdentityLoginViaAwsAuth
- case infisicalSpec.Auth.TokenAuthCredentials != nil:
- loginFn = performTokenAuthLogin
- authMethod = machineIdentityLoginViaTokenAuth
- default:
- cancelSdkClient()
- return nil, errors.New("authentication method not found")
- }
- if err := loginFn(ctx, store, infisicalSpec, sdkClient, kube, namespace); err != nil {
- cancelSdkClient()
- return nil, err
- }
- return &Provider{
- cancelSdkClient: cancelSdkClient,
- sdkClient: sdkClient,
- apiScope: &ClientScope{
- EnvironmentSlug: infisicalSpec.SecretsScope.EnvironmentSlug,
- ProjectSlug: infisicalSpec.SecretsScope.ProjectSlug,
- Recursive: infisicalSpec.SecretsScope.Recursive,
- SecretPath: secretPath,
- ExpandSecretReferences: infisicalSpec.SecretsScope.ExpandSecretReferences,
- },
- authMethod: authMethod,
- }, nil
- }
- // Close releases any resources used by the provider.
- func (p *Provider) Close(_ context.Context) error {
- p.cancelSdkClient()
- // Don't revoke token if token auth was used
- if p.authMethod == machineIdentityLoginViaTokenAuth {
- return nil
- }
- err := p.sdkClient.Auth().RevokeAccessToken()
- metrics.ObserveAPICall(constants.ProviderName, revokeAccessToken, err)
- return err
- }
- // GetStoreSecretData retrieves secret data from a Kubernetes secret using the provided reference.
- // It handles namespace resolution and returns the secret value as a string.
- func GetStoreSecretData(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string, secret esmeta.SecretKeySelector) (string, error) {
- secretRef := esmeta.SecretKeySelector{
- Name: secret.Name,
- Key: secret.Key,
- }
- if secret.Namespace != nil {
- secretRef.Namespace = secret.Namespace
- }
- secretData, err := resolvers.SecretKeyRef(ctx, kube, store.GetObjectKind().GroupVersionKind().Kind, namespace, &secretRef)
- if err != nil {
- return "", err
- }
- return secretData, nil
- }
- // ValidateStore validates the Infisical SecretStore configuration.
- // It checks for required fields and valid authentication settings.
- func (p *Provider) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
- storeSpec := store.GetSpec()
- infisicalStoreSpec := storeSpec.Provider.Infisical
- if infisicalStoreSpec == nil {
- return nil, errors.New("invalid infisical store")
- }
- if err := validateSecretsScope(infisicalStoreSpec); err != nil {
- return nil, err
- }
- if err := validateCAProvider(store, infisicalStoreSpec); err != nil {
- return nil, err
- }
- if err := validateUniversalAuth(store, infisicalStoreSpec); err != nil {
- return nil, err
- }
- return nil, nil
- }
- func validateSecretsScope(spec *esv1.InfisicalProvider) error {
- if spec.SecretsScope.EnvironmentSlug == "" || spec.SecretsScope.ProjectSlug == "" {
- return errors.New("secretsScope.projectSlug and secretsScope.environmentSlug cannot be empty")
- }
- return nil
- }
- func validateCAProvider(store esv1.GenericStore, spec *esv1.InfisicalProvider) error {
- if spec.CAProvider == nil {
- return nil
- }
- storeKind := store.GetObjectKind().GroupVersionKind().Kind
- if storeKind == esv1.ClusterSecretStoreKind && spec.CAProvider.Namespace == nil {
- return errors.New("caProvider.namespace is required for ClusterSecretStore")
- }
- if storeKind == esv1.SecretStoreKind && spec.CAProvider.Namespace != nil {
- return errors.New("caProvider.namespace must be empty with SecretStore")
- }
- return nil
- }
- func validateUniversalAuth(store esv1.GenericStore, spec *esv1.InfisicalProvider) error {
- if spec.Auth.UniversalAuthCredentials == nil {
- return nil
- }
- uaCredential := spec.Auth.UniversalAuthCredentials
- if err := esutils.ValidateReferentSecretSelector(store, uaCredential.ClientID); err != nil {
- return err
- }
- if err := esutils.ValidateReferentSecretSelector(store, uaCredential.ClientSecret); err != nil {
- return err
- }
- if uaCredential.ClientID.Key == "" || uaCredential.ClientSecret.Key == "" {
- return errors.New("universalAuthCredentials.clientId and universalAuthCredentials.clientSecret cannot be empty")
- }
- return nil
- }
- // NewProvider creates a new Provider instance.
- func NewProvider() esv1.Provider {
- return &Provider{}
- }
- // ProviderSpec returns the provider specification for registration.
- func ProviderSpec() *esv1.SecretStoreProvider {
- return &esv1.SecretStoreProvider{
- Infisical: &esv1.InfisicalProvider{},
- }
- }
- // MaintenanceStatus returns the maintenance status of the provider.
- func MaintenanceStatus() esv1.MaintenanceStatus {
- return esv1.MaintenanceStatusMaintained
- }
|