func (d *InstallReconciler) genCmdRemoveOldConfig() []string {
	return []string{
		"mv",
		paths.AdminToolsConf,
		fmt.Sprintf("%s.uid.%s", paths.AdminToolsConf, string(d.Vdb.UID)),
	}
}
genCmdRemoveOldConfig generates the command to remove the old admintools.conf file
package controllers
import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"github.com/go-logr/logr"
	"github.com/lithammer/dedent"
	vapi "github.com/vertica/vertica-kubernetes/api/v1beta1"
	"github.com/vertica/vertica-kubernetes/pkg/atconf"
	"github.com/vertica/vertica-kubernetes/pkg/cmds"
	"github.com/vertica/vertica-kubernetes/pkg/names"
	"github.com/vertica/vertica-kubernetes/pkg/paths"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
)
type InstallReconciler struct {
	VRec     *VerticaDBReconciler
	Log      logr.Logger
	Vdb      *vapi.VerticaDB  
	PRunner  cmds.PodRunner
	PFacts   *PodFacts
	ATWriter atconf.Writer
}
func MakeInstallReconciler(vdbrecon *VerticaDBReconciler, log logr.Logger,
	vdb *vapi.VerticaDB, prunner cmds.PodRunner, pfacts *PodFacts) ReconcileActor {
	return &InstallReconciler{
		VRec:     vdbrecon,
		Log:      log,
		Vdb:      vdb,
		PRunner:  prunner,
		PFacts:   pfacts,
		ATWriter: atconf.MakeFileWriter(log, vdb, prunner),
	}
}
func (d *InstallReconciler) Reconcile(ctx context.Context, req *ctrl.Request) (ctrl.Result, error) {
	if d.Vdb.Spec.InitPolicy == vapi.CommunalInitPolicyScheduleOnly {
		return ctrl.Result{}, nil
	}
	if err := d.PFacts.Collect(ctx, d.Vdb); err != nil {
		return ctrl.Result{}, err
	}
	return d.analyzeFacts(ctx)
}
func (d *InstallReconciler) analyzeFacts(ctx context.Context) (ctrl.Result, error) {
	if ok, podNotRunning := d.PFacts.anyPodsNotRunning(); ok {
		d.Log.Info("At least one pod isn't running.  Aborting the install.", "pod", podNotRunning)
		return ctrl.Result{Requeue: true}, nil
	}
	fns := []func(context.Context) error{
		d.acceptEulaIfMissing,
		d.checkConfigDir,
		d.addHostsToATConf,
	}
	for _, fn := range fns {
		if err := fn(ctx); err != nil {
			return ctrl.Result{}, err
		}
	}
	return ctrl.Result{}, nil
}
func (d *InstallReconciler) addHostsToATConf(ctx context.Context) error {
	pods, err := d.getInstallTargets(ctx)
	if err != nil {
		return err
	}
	if len(pods) == 0 {
		return nil
	}
	installedPods := d.PFacts.findInstalledPods()
	ipsToInstall := []string{}
	for _, p := range pods {
		ipsToInstall = append(ipsToInstall, p.podIP)
	}
	installPod := types.NamespacedName{}
	if len(installedPods) != 0 {
		installPod, err = findATBasePod(d.Vdb, d.PFacts)
		if err != nil {
			return err
		}
	}
	atConfTempFile, err := d.ATWriter.AddHosts(ctx, installPod, ipsToInstall)
	if err != nil {
		return err
	}
	defer os.Remove(atConfTempFile)
	debugDumpAdmintoolsConfForPods(ctx, d.PRunner, installedPods)
	if err := distributeAdmintoolsConf(ctx, d.Vdb, d.VRec, d.PFacts, d.PRunner, atConfTempFile); err != nil {
		return err
	}
	installedPods = append(installedPods, pods...)
	debugDumpAdmintoolsConfForPods(ctx, d.PRunner, installedPods)
	d.PFacts.Invalidate()
	return d.createInstallIndicators(ctx, pods)
}
func (d *InstallReconciler) acceptEulaIfMissing(ctx context.Context) error {
	for _, p := range d.PFacts.Detail {
		if !p.eulaAccepted.IsFalse() || !p.isPodRunning {
			continue
		}
		if err := d.acceptEulaInPod(ctx, p); err != nil {
			return err
		}
	}
	return nil
}
func (d *InstallReconciler) checkConfigDir(ctx context.Context) error {
	for _, p := range d.PFacts.Detail {
		if !p.isPodRunning {
			continue
		}
		if p.configLogrotateExists && !p.configLogrotateWritable {
			_, _, err := d.PRunner.ExecInPod(ctx, p.name, names.ServerContainer,
				"sudo", "chown", "-R", "dbadmin:verticadba", paths.ConfigLogrotatePath)
			if err != nil {
				return err
			}
		}
		if !p.configShareExists {
			_, _, err := d.PRunner.ExecInPod(ctx, p.name, names.ServerContainer,
				"mkdir", paths.ConfigSharePath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func (d *InstallReconciler) getInstallTargets(ctx context.Context) ([]*PodFact, error) {
	podList := make([]*PodFact, 0, len(d.PFacts.Detail))
	for _, v := range d.PFacts.Detail {
		if v.isInstalled.IsFalse() && v.dbExists.IsFalse() {
			podList = append(podList, v)
			if v.hasStaleAdmintoolsConf {
				if _, _, err := d.PRunner.ExecInPod(ctx, v.name, names.ServerContainer, d.genCmdRemoveOldConfig()...); err != nil {
					return podList, fmt.Errorf("failed to remove old admintools.conf: %w", err)
				}
			}
		}
	}
	return podList, nil
}
func (d *InstallReconciler) createInstallIndicators(ctx context.Context, pods []*PodFact) error {
	for _, v := range pods {
		compat21Node, err := d.fetchCompat21NodeNum(ctx, v)
		if err != nil {
			return fmt.Errorf("failed to extract compat21 node name: %w", err)
		}
		d.Log.Info("create installer indicator file", "Pod", v.name)
		cmd := d.genCmdCreateInstallIndicator(compat21Node)
		if stdout, _, err := d.PRunner.ExecInPod(ctx, v.name, names.ServerContainer, cmd...); err != nil {
			return fmt.Errorf("failed to create installer indicator with command '%s', output was '%s': %w", cmd, stdout, err)
		}
	}
	return nil
}
func (d *InstallReconciler) fetchCompat21NodeNum(ctx context.Context, pf *PodFact) (string, error) {
	cmd := []string{
		"bash", "-c", fmt.Sprintf("grep -E '^node[0-9]{4} = %s,' %s", pf.podIP, paths.AdminToolsConf),
	}
	var stdout string
	var err error
	if stdout, _, err = d.PRunner.ExecInPod(ctx, pf.name, names.ServerContainer, cmd...); err != nil {
		return "", fmt.Errorf("failed to find compat21 node for IP '%s', output was '%s': %w", pf.podIP, stdout, err)
	}
	re := regexp.MustCompile(`^(node\d{4}) = .*`)
	match := re.FindAllStringSubmatch(stdout, 1)
	if len(match) > 0 && len(match[0]) > 0 {
		return match[0][1], nil
	}
	return "", fmt.Errorf("could not find compat21 node in output")
}
func (d *InstallReconciler) genCmdCreateInstallIndicator(compat21Node string) []string {
	return []string{"bash", "-c", fmt.Sprintf("echo %s > %s", compat21Node, d.Vdb.GenInstallerIndicatorFileName())}
}