func (d *Deployment) GetTLSKeyfile(group api.ServerGroup, member api.MemberStatus) (string, error) {
	secretName := k8sutil.CreateTLSKeyfileSecretName(d.GetName(), group.AsRole(), member.ID)
	result, err := k8sutil.GetTLSKeyfileSecret(d.GetCachedStatus().SecretReadInterface(), secretName)
	if err != nil {
		return "", errors.WithStack(err)
	}
	return result, nil
}
GetTLSKeyfile returns the keyfile encoded TLS certificate+key for
the given member.
package deployment
import (
	"context"
	"crypto/tls"
	"net"
	nhttp "net/http"
	"strconv"
	"time"
	"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/arangomember"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/persistentvolumeclaim"
	podMod "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/pod"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/poddisruptionbudget"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/serviceaccount"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/servicemonitor"
	"github.com/arangodb/kube-arangodb/pkg/deployment/reconcile"
	"github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
	inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret"
	"github.com/arangodb/kube-arangodb/pkg/util/errors"
	"github.com/arangodb/kube-arangodb/pkg/util/arangod/conn"
	"github.com/arangodb/kube-arangodb/pkg/operator/scope"
	monitoringClient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/typed/monitoring/v1"
	"github.com/arangodb/kube-arangodb/pkg/deployment/features"
	"github.com/arangodb/go-driver/http"
	"github.com/arangodb/go-driver/jwt"
	"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
	"github.com/arangodb/kube-arangodb/pkg/util/constants"
	apiErrors "k8s.io/apimachinery/pkg/api/errors"
	"github.com/arangodb/arangosync-client/client"
	"github.com/arangodb/arangosync-client/tasks"
	driver "github.com/arangodb/go-driver"
	"github.com/arangodb/go-driver/agency"
	"github.com/rs/zerolog/log"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
	api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
	"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
	"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
	core "k8s.io/api/core/v1"
)
var _ resources.Context = &Deployment{}
func (d *Deployment) GetBackup(ctx context.Context, backup string) (*backupApi.ArangoBackup, error) {
	ctxChild, cancel := context.WithTimeout(ctx, k8sutil.GetRequestTimeout())
	defer cancel()
	return d.deps.DatabaseCRCli.BackupV1().ArangoBackups(d.Namespace()).Get(ctxChild, backup, meta.GetOptions{})
}
func (d *Deployment) GetAPIObject() k8sutil.APIObject {
	return d.apiObject
}
func (d *Deployment) GetServerGroupIterator() resources.ServerGroupIterator {
	return d.apiObject
}
func (d *Deployment) getKubeCli() kubernetes.Interface {
	return d.deps.KubeCli
}
func (d *Deployment) getMonitoringV1Cli() monitoringClient.MonitoringV1Interface {
	return d.deps.KubeMonitoringCli
}
func (d *Deployment) getArangoCli() versioned.Interface {
	return d.deps.DatabaseCRCli
}
func (d *Deployment) GetScope() scope.Scope {
	return d.config.Scope
}
func (d *Deployment) GetOperatorImage() string {
	return d.config.OperatorImage
}
func (d *Deployment) GetNamespace() string {
	return d.namespace
}
func (d *Deployment) GetPhase() api.DeploymentPhase {
	return d.status.last.Phase
}
func (d *Deployment) GetSpec() api.DeploymentSpec {
	return d.apiObject.Spec
}
func (d *Deployment) GetDeploymentHealth() (driver.ClusterHealth, error) {
	return d.resources.GetDeploymentHealth()
}
func (d *Deployment) GetStatus() (api.DeploymentStatus, int32) {
	d.status.mutex.Lock()
	defer d.status.mutex.Unlock()
	return d.getStatus()
}
func (d *Deployment) getStatus() (api.DeploymentStatus, int32) {
	version := d.status.version
	return *d.status.last.DeepCopy(), version
}
func (d *Deployment) UpdateStatus(ctx context.Context, status api.DeploymentStatus, lastVersion int32, force ...bool) error {
	d.status.mutex.Lock()
	defer d.status.mutex.Unlock()
	return d.updateStatus(ctx, status, lastVersion, force...)
}
func (d *Deployment) updateStatus(ctx context.Context, status api.DeploymentStatus, lastVersion int32, force ...bool) error {
	if d.status.version != lastVersion {
		d.deps.Log.Error().
			Int32("expected-version", lastVersion).
			Int32("actual-version", d.status.version).
			Msg("UpdateStatus version conflict error.")
		return errors.WithStack(errors.Newf("Status conflict error. Expected version %d, got %d", lastVersion, d.status.version))
	}
	d.status.version++
	d.status.last = *status.DeepCopy()
	if err := d.updateCRStatus(ctx, force...); err != nil {
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) UpdateMember(ctx context.Context, member api.MemberStatus) error {
	status, lastVersion := d.GetStatus()
	_, group, found := status.Members.ElementByID(member.ID)
	if !found {
		return errors.WithStack(errors.Newf("Member %s not found", member.ID))
	}
	if err := status.Members.Update(member, group); err != nil {
		return errors.WithStack(err)
	}
	if err := d.UpdateStatus(ctx, status, lastVersion); err != nil {
		d.deps.Log.Debug().Err(err).Msg("Updating CR status failed")
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) GetDatabaseClient(ctx context.Context) (driver.Client, error) {
	c, err := d.clientCache.GetDatabase(ctx)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return c, nil
}
func (d *Deployment) GetServerClient(ctx context.Context, group api.ServerGroup, id string) (driver.Client, error) {
	c, err := d.clientCache.Get(ctx, group, id)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return c, nil
}
func (d *Deployment) GetAuthentication() conn.Auth {
	return d.clientCache.GetAuth()
}
func (d *Deployment) GetAgencyClients(ctx context.Context, predicate func(id string) bool) ([]driver.Connection, error) {
	agencyMembers := d.status.last.Members.Agents
	result := make([]driver.Connection, 0, len(agencyMembers))
	for _, m := range agencyMembers {
		if predicate != nil && !predicate(m.ID) {
			continue
		}
		client, err := d.GetServerClient(ctx, api.ServerGroupAgents, m.ID)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		conn := client.Connection()
		result = append(result, conn)
	}
	return result, nil
}
func (d *Deployment) GetAgency(ctx context.Context) (agency.Agency, error) {
	return d.clientCache.GetAgency(ctx)
}
func (d *Deployment) getConnConfig() (http.ConnectionConfig, error) {
	transport := &nhttp.Transport{
		Proxy: nhttp.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 100 * time.Millisecond,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       100 * time.Millisecond,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
	if d.apiObject.Spec.TLS.IsSecure() {
		transport.TLSClientConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}
	connConfig := http.ConnectionConfig{
		Transport:          transport,
		DontFollowRedirect: true,
	}
	return connConfig, nil
}
func (d *Deployment) getAuth() (driver.Authentication, error) {
	if !d.apiObject.Spec.Authentication.IsAuthenticated() {
		return nil, nil
	}
	var secret string
	if i := d.apiObject.Status.CurrentImage; i == nil || !features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
		s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), d.apiObject.Spec.Authentication.GetJWTSecretName(), meta.GetOptions{})
		if err != nil {
			return nil, errors.Newf("JWT Secret is missing")
		}
		jwt, ok := s.Data[constants.SecretKeyToken]
		if !ok {
			return nil, errors.Newf("JWT Secret is invalid")
		}
		secret = string(jwt)
	} else {
		s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), pod.JWTSecretFolder(d.GetName()), meta.GetOptions{})
		if err != nil {
			d.deps.Log.Error().Err(err).Msgf("Unable to get secret")
			return nil, errors.Newf("JWT Folder Secret is missing")
		}
		if len(s.Data) == 0 {
			return nil, errors.Newf("JWT Folder Secret is empty")
		}
		if q, ok := s.Data[pod.ActiveJWTKey]; ok {
			secret = string(q)
		} else {
			for _, q := range s.Data {
				secret = string(q)
				break
			}
		}
	}
	jwt, err := jwt.CreateArangodJwtAuthorizationHeader(secret, "kube-arangodb")
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return driver.RawAuthentication(jwt), nil
}
func (d *Deployment) GetSyncServerClient(ctx context.Context, group api.ServerGroup, id string) (client.API, error) {
	log := d.deps.Log
	secretName := d.apiObject.Spec.Sync.Monitoring.GetTokenSecretName()
	monitoringToken, err := k8sutil.GetTokenSecret(ctx, d.GetCachedStatus().SecretReadInterface(), secretName)
	if err != nil {
		log.Debug().Err(err).Str("secret-name", secretName).Msg("Failed to get sync monitoring secret")
		return nil, errors.WithStack(err)
	}
	dnsName := k8sutil.CreatePodDNSNameWithDomain(d.apiObject, d.apiObject.Spec.ClusterDomain, group.AsRole(), id)
	port := k8sutil.ArangoSyncMasterPort
	if group == api.ServerGroupSyncWorkers {
		port = k8sutil.ArangoSyncWorkerPort
	}
	source := client.Endpoint{"https://" + net.JoinHostPort(dnsName, strconv.Itoa(port))}
	tlsAuth := tasks.TLSAuthentication{
		TLSClientAuthentication: tasks.TLSClientAuthentication{
			ClientToken: monitoringToken,
		},
	}
	auth := client.NewAuthentication(tlsAuth, "")
	insecureSkipVerify := true
	c, err := d.syncClientCache.GetClient(d.deps.Log, source, auth, insecureSkipVerify)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return c, nil
}
func (d *Deployment) CreateMember(ctx context.Context, group api.ServerGroup, id string, mods ...reconcile.CreateMemberMod) (string, error) {
	log := d.deps.Log
	if err := d.WithStatusUpdateErr(ctx, func(s *api.DeploymentStatus) (bool, error) {
		nid, err := createMember(log, s, group, id, d.apiObject, mods...)
		if err != nil {
			log.Debug().Err(err).Str("group", group.AsRole()).Msg("Failed to create member")
			return false, errors.WithStack(err)
		}
		id = nid
		return true, nil
	}); err != nil {
		return "", err
	}
	d.CreateEvent(k8sutil.NewMemberAddEvent(id, group.AsRole(), d.apiObject))
	return id, nil
}
func (d *Deployment) GetPod(ctx context.Context, podName string) (*core.Pod, error) {
	return d.GetCachedStatus().PodReadInterface().Get(ctx, podName, meta.GetOptions{})
}
func (d *Deployment) DeletePod(ctx context.Context, podName string) error {
	log := d.deps.Log
	err := k8sutil.RunWithTimeout(ctx, func(ctxChild context.Context) error {
		return d.PodsModInterface().Delete(ctxChild, podName, meta.DeleteOptions{})
	})
	if err != nil && !k8sutil.IsNotFound(err) {
		log.Debug().Err(err).Str("pod", podName).Msg("Failed to remove pod")
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) CleanupPod(ctx context.Context, p *core.Pod) error {
	log := d.deps.Log
	podName := p.GetName()
	options := meta.NewDeleteOptions(0)
	options.Preconditions = meta.NewUIDPreconditions(string(p.GetUID()))
	err := k8sutil.RunWithTimeout(ctx, func(ctxChild context.Context) error {
		return d.PodsModInterface().Delete(ctxChild, podName, *options)
	})
	if err != nil && !k8sutil.IsNotFound(err) {
		log.Debug().Err(err).Str("pod", podName).Msg("Failed to cleanup pod")
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) RemovePodFinalizers(ctx context.Context, podName string) error {
	log := d.deps.Log
	ctxChild, cancel := context.WithTimeout(ctx, k8sutil.GetRequestTimeout())
	defer cancel()
	p, err := d.GetCachedStatus().PodReadInterface().Get(ctxChild, podName, meta.GetOptions{})
	if err != nil {
		if k8sutil.IsNotFound(err) {
			return nil
		}
		return errors.WithStack(err)
	}
	err = k8sutil.RemovePodFinalizers(ctx, d.GetCachedStatus(), log, d.PodsModInterface(), p, p.GetFinalizers(), true)
	if err != nil {
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) DeletePvc(ctx context.Context, pvcName string) error {
	log := d.deps.Log
	err := k8sutil.RunWithTimeout(ctx, func(ctxChild context.Context) error {
		return d.PersistentVolumeClaimsModInterface().Delete(ctxChild, pvcName, meta.DeleteOptions{})
	})
	if err != nil && !k8sutil.IsNotFound(err) {
		log.Debug().Err(err).Str("pvc", pvcName).Msg("Failed to remove pvc")
		return errors.WithStack(err)
	}
	return nil
}
func (d *Deployment) UpdatePvc(ctx context.Context, pvc *core.PersistentVolumeClaim) error {
	err := k8sutil.RunWithTimeout(ctx, func(ctxChild context.Context) error {
		_, err := d.PersistentVolumeClaimsModInterface().Update(ctxChild, pvc, meta.UpdateOptions{})
		return err
	})
	if err == nil {
		return nil
	}
	if apiErrors.IsNotFound(err) {
		return nil
	}
	return errors.WithStack(err)
}
func (d *Deployment) GetOwnedPVCs() ([]core.PersistentVolumeClaim, error) {
	pvcs := d.GetCachedStatus().PersistentVolumeClaims()
	myPVCs := make([]core.PersistentVolumeClaim, 0, len(pvcs))
	for _, p := range pvcs {
		if d.isOwnerOf(p) {
			myPVCs = append(myPVCs, *p)
		}
	}
	return myPVCs, nil
}
func (d *Deployment) GetPvc(ctx context.Context, pvcName string) (*core.PersistentVolumeClaim, error) {
	ctxChild, cancel := context.WithTimeout(ctx, k8sutil.GetRequestTimeout())
	defer cancel()
	pvc, err := d.GetCachedStatus().PersistentVolumeClaimReadInterface().Get(ctxChild, pvcName, meta.GetOptions{})
	if err != nil {
		log.Debug().Err(err).Str("pvc-name", pvcName).Msg("Failed to get PVC")
		return nil, errors.WithStack(err)
	}
	return pvc, nil
}