func Main()

in cache-config/t3c-apply/t3c-apply.go [90:373]


func Main() int {

	var syncdsUpdate torequest.UpdateStatus
	var lock util.FileLock
	cfg, err := config.GetCfg(Version, GitRevision)
	if err != nil {
		log.Infoln(err)
		log.Errorln(FailureExitMsg)
		return ExitCodeConfigError
	} else if cfg == (config.Cfg{}) { // user used the --help option
		return ExitCodeSuccess
	}

	log.Infoln("Trying to acquire app lock")
	for lockStart := time.Now(); !lock.GetLock(LockFilePath); {
		if time.Since(lockStart) > LockFileRetryTimeout {
			log.Errorf("Failed to get app lock after %v seconds, another instance is running, exiting without running\n", int(LockFileRetryTimeout/time.Second))
			log.Infoln(FailureExitMsg)
			return ExitCodeAlreadyRunning
		}
		log.Errorf("Unable to acquire app lock retrying in: %v ", LockFileRetryInterval)
		time.Sleep(LockFileRetryInterval)
	}
	log.Infoln("Acquired app lock")
	defer lock.Unlock()

	// Note failing to load old metadata is not fatal!
	// oldMetaData must always be checked for nil before usage!
	oldMetaData, err := LoadMetaData(cfg)
	if err != nil {
		log.Errorln("Failed to load old metadata file, metadata-dependent functionality disabled: " + err.Error())
	}

	// Note we only write the metadata file after acquiring the app lock.
	// We don't want to write a metadata file if we didn't run because another t3c-apply
	// was already running.
	metaData := t3cutil.NewApplyMetaData()

	metaData.ServerHostName = cfg.CacheHostName

	t3cutil.WriteActionLog(t3cutil.ActionLogActionApplyStart, t3cutil.ActionLogStatusSuccess, metaData)

	if cfg.UseGit == config.UseGitYes {
		triedMakingRepo, err := util.EnsureConfigDirIsGitRepo(cfg)
		if err != nil {
			log.Errorln("Ensuring config directory '" + cfg.TsConfigDir + "' is a git repo - config may not be a git repo! " + err.Error())
			if triedMakingRepo {
				t3cutil.WriteActionLog(t3cutil.ActionLogActionGitInit, t3cutil.ActionLogStatusFailure, metaData)
			}
		} else {
			log.Infoln("Successfully ensured ATS config directory '" + cfg.TsConfigDir + "' is a git repo")
			if triedMakingRepo {
				t3cutil.WriteActionLog(t3cutil.ActionLogActionGitInit, t3cutil.ActionLogStatusSuccess, metaData)
			}
		}

	} else {
		log.Infoln("UseGit not 'yes', not creating git repo")
	}

	if cfg.UseGit == config.UseGitYes || cfg.UseGit == config.UseGitAuto {
		//need to see if there is an old lock file laying around.
		//older than 5 minutes
		const gitMaxLockAgeMinutes = 5
		const gitLock = ".git/index.lock"
		gitLockFile := filepath.Join(cfg.TsConfigDir, gitLock)
		oldLock, err := util.IsGitLockFileOld(gitLockFile, time.Now(), gitMaxLockAgeMinutes*time.Minute)
		if err != nil {
			log.Errorln("checking for git lock file: " + err.Error())
		}
		if oldLock {
			log.Errorf("removing git lock file older than %dm", gitMaxLockAgeMinutes)
			err := util.RemoveGitLock(gitLockFile)
			if err != nil {
				log.Errorf("couldn't remove git lock file: %v", err.Error())
			}
		}
		log.Infoln("Checking git for safe directory config")
		if err := util.GetGitConfigSafeDir(cfg); err != nil {
			log.Warnln("error checking git for safe directory config: " + err.Error())
		}
		// commit anything someone else changed when we weren't looking,
		// with a keyword indicating it wasn't our change
		if err := util.MakeGitCommitAll(cfg, util.GitChangeNotSelf, true); err != nil {
			log.Errorln("git committing existing changes, dir '" + cfg.TsConfigDir + "': " + err.Error())
			t3cutil.WriteActionLog(t3cutil.ActionLogActionGitCommitInitial, t3cutil.ActionLogStatusFailure, metaData)
		} else {
			t3cutil.WriteActionLog(t3cutil.ActionLogActionGitCommitInitial, t3cutil.ActionLogStatusSuccess, metaData)
		}
	}

	trops := torequest.NewTrafficOpsReq(cfg)

	// if doing os checks, insure there is a 'systemctl' or 'service' and 'chkconfig' commands.
	if !cfg.SkipOSCheck && cfg.SvcManagement == config.Unknown {
		log.Errorln("OS checks are enabled and unable to find any know service management tools.")
	}

	// create and clean the config.TmpBase (/tmp/ort)
	if !util.MkDir(config.TmpBase, cfg.ReportOnly) {
		log.Errorln("mkdir TmpBase '" + config.TmpBase + "' failed, cannot continue")
		log.Infoln(FailureExitMsg)
		return ExitCodeGeneralFailure
	} else if !util.CleanTmpDir(cfg) {
		log.Errorln("CleanTmpDir failed, cannot continue")
		log.Infoln(FailureExitMsg)
		return ExitCodeGeneralFailure
	}

	log.Infoln(time.Now().Format(time.RFC3339))

	if !util.CheckUser(cfg) {

		lock.Unlock()
		return ExitCodeUserCheckError
	}

	// if running in Revalidate mode, check to see if it's
	// necessary to continue
	if cfg.Files == t3cutil.ApplyFilesFlagReval {
		syncdsUpdate, err = trops.CheckRevalidateState(false)

		if err != nil {
			log.Errorln("Checking revalidate state: " + err.Error())
			return GitCommitAndExit(ExitCodeRevalidationError, FailureExitMsg, cfg, metaData, oldMetaData)
		}
		if syncdsUpdate == torequest.UpdateTropsNotNeeded {
			log.Infoln("Checking revalidate state: returned UpdateTropsNotNeeded")
			metaData.Succeeded = true
			return GitCommitAndExit(ExitCodeRevalidationError, SuccessExitMsg, cfg, metaData, oldMetaData)
		}

	} else {
		syncdsUpdate, err = trops.CheckSyncDSState(metaData, cfg)
		if err != nil {
			log.Errorln("Checking syncds state: " + err.Error())
			return GitCommitAndExit(ExitCodeSyncDSError, FailureExitMsg, cfg, metaData, oldMetaData)
		}
		if !cfg.IgnoreUpdateFlag && cfg.Files == t3cutil.ApplyFilesFlagAll && syncdsUpdate == torequest.UpdateTropsNotNeeded {
			// If touching remap.config fails, we want to still try to restart services
			// But log a critical-post-config-failure, which needs logged right before exit.
			postConfigFail := false
			// check for maxmind db updates even if we have no other updates
			if CheckMaxmindUpdate(cfg) {
				// We updated the db so we should touch and reload
				trops.RemapConfigReload = true
				path := cfg.TsConfigDir + "/remap.config"
				_, rc, err := util.ExecCommand("/usr/bin/touch", path)
				if err != nil {
					log.Errorf("failed to update the remap.config for reloading: %s\n", err.Error())
					postConfigFail = true
				} else if rc == 0 {
					log.Infoln("updated the remap.config for reloading.")
				}
				if err := trops.StartServices(&syncdsUpdate, metaData, cfg); err != nil {
					log.Errorln("failed to start services: " + err.Error())
					metaData.PartialSuccess = true
					return GitCommitAndExit(ExitCodeServicesError, PostConfigFailureExitMsg, cfg, metaData, oldMetaData)
				}
			}
			finalMsg := SuccessExitMsg
			if postConfigFail {
				finalMsg = PostConfigFailureExitMsg
			}
			metaData.Succeeded = true
			return GitCommitAndExit(ExitCodeSuccess, finalMsg, cfg, metaData, oldMetaData)
		}
	}

	if cfg.Files != t3cutil.ApplyFilesFlagAll {
		// make sure we got the data necessary to check packages
		log.Infoln("======== Didn't get all files, no package processing needed or possible ========")
		metaData.InstalledPackages = oldMetaData.InstalledPackages
	} else if cfg.RpmDBOk {
		log.Infoln("======== Start processing packages  ========")
		err = trops.ProcessPackages()
		if err != nil {
			log.Errorf("Error processing packages: %s\n", err)
			return GitCommitAndExit(ExitCodePackagingError, FailureExitMsg, cfg, metaData, oldMetaData)
		}
		metaData.InstalledPackages = t3cutil.PackagesToMetaData(trops.Pkgs)

		// check and make sure packages are enabled for startup
		err = trops.CheckSystemServices()
		if err != nil {
			log.Errorf("Error verifying system services: %s\n", err.Error())
			return GitCommitAndExit(ExitCodeServicesError, FailureExitMsg, cfg, metaData, oldMetaData)
		}
	} else {
		log.Warnln("======== RPM DB checks failed, package processing not possible, using installed packages from  metadata if available========")
		trops.ProcessPackagesWithMetaData(oldMetaData.InstalledPackages)
	}

	log.Debugf("Preparing to fetch the config files for %s, files: %s, syncdsUpdate: %s\n", cfg.CacheHostName, cfg.Files, syncdsUpdate)
	err = trops.GetConfigFileList()
	if err != nil {
		log.Errorf("Getting config file list: %s\n", err)
		return GitCommitAndExit(ExitCodeConfigFilesError, FailureExitMsg, cfg, metaData, oldMetaData)
	}
	syncdsUpdate, err = trops.ProcessConfigFiles(metaData)
	if err != nil {
		log.Errorf("Error while processing config files: %s\n", err.Error())
		t3cutil.WriteActionLog(t3cutil.ActionLogActionUpdateFilesAll, t3cutil.ActionLogStatusFailure, metaData)

	} else {
		t3cutil.WriteActionLog(t3cutil.ActionLogActionUpdateFilesAll, t3cutil.ActionLogStatusSuccess, metaData)
	}

	// check for maxmind db updates
	// If we've updated also reload remap to reload the plugin and pick up the new database
	if CheckMaxmindUpdate(cfg) {
		trops.RemapConfigReload = true
	}

	if trops.RemapConfigReload == true {
		cfg, ok := trops.GetConfigFile("remap.config")
		_, rc, err := util.ExecCommand("/usr/bin/touch", cfg.Path)
		if err != nil {
			log.Errorf("failed to update the remap.config for reloading: %s\n", err.Error())
		} else if rc == 0 && ok == true {
			log.Infoln("updated the remap.config for reloading.")
		}
	}

	if err := trops.StartServices(&syncdsUpdate, metaData, cfg); err != nil {
		log.Errorln("failed to start services: " + err.Error())
		metaData.PartialSuccess = true
		return GitCommitAndExit(ExitCodeServicesError, PostConfigFailureExitMsg, cfg, metaData, oldMetaData)
	}

	// start 'teakd' if installed.
	if trops.IsPackageInstalled("teakd") {
		svcStatus, pid, err := util.GetServiceStatus("teakd")
		if err != nil {
			log.Errorf("not starting 'teakd', error getting 'teakd' run status: %s\n", err)
		} else if svcStatus == util.SvcNotRunning {
			running, err := util.ServiceStart("teakd", "start")
			if err != nil {
				log.Errorf("'teakd' was not started: %s\n", err)
			} else if running {
				log.Infoln("service 'teakd' started.")
			} else if svcStatus == util.SvcRunning {
				log.Infof("service 'teakd' was already running, pid: %v\n", pid)
			}
		}
	}

	if trops.HitchReload {
		svcStatus, _, err := util.GetServiceStatus("hitch")
		cmd := "start"
		running := false
		if err != nil {
			log.Errorf("not starting 'hitch', error getting 'hitch' run status: %s\n", err)
		} else if svcStatus != util.SvcNotRunning {
			cmd = "reload"
		}
		running, err = util.ServiceStart("hitch", cmd)
		if err != nil {
			log.Errorf("'hitch' was not %sed: %s\n", cmd, err)
		} else if running {
			log.Infof("service 'hitch' %sed", cmd)
		} else {
			log.Infoln("service 'hitch' already running")
		}
	}

	// reload sysctl
	if trops.SysCtlReload == true {
		runSysctl(cfg)
	}

	trops.PrintWarnings()

	if err := trops.UpdateTrafficOps(&syncdsUpdate); err != nil {
		log.Errorf("failed to update Traffic Ops: %s\n", err.Error())
	}

	metaData.Succeeded = true
	if syncdsUpdate == torequest.UpdateTropsFailed {
		return GitCommitAndExit(ExitCodeSuccess, CacheConfigFailureExitMsg, cfg, metaData, oldMetaData)
	} else {
		return GitCommitAndExit(ExitCodeSuccess, SuccessExitMsg, cfg, metaData, oldMetaData)
	}
}